From 6d17ab8015300760370e2ff50bf0a54ba06d03c6 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Sep 2013 16:51:22 +0000 Subject: [PATCH 001/173] Port wginfo/ to new schema, delete some dead code. Missing tests and still a little bit of cruft left from views in other modules. - Legacy-Id: 6084 --- ietf/templates/doc/document_charter.html | 2 +- .../wginfo/1wg-charters-by-acronym.txt | 8 +- ietf/templates/wginfo/1wg-charters.txt | 9 +- .../wginfo/1wg-summary-by-acronym.txt | 12 +- ietf/templates/wginfo/1wg-summary.txt | 19 +- .../{wg-dirREDESIGN.html => active_wgs.html} | 0 ietf/templates/wginfo/bofs.html | 2 +- ietf/templates/wginfo/chartering_wgs.html | 2 +- ietf/templates/wginfo/conclude.html | 2 +- ietf/templates/wginfo/edit.html | 2 +- ietf/templates/wginfo/edit_milestones.html | 4 +- ietf/templates/wginfo/group_base.html | 99 ++++++ ietf/templates/wginfo/group_charter.html | 171 +++++++++++ ietf/templates/wginfo/group_documents.html | 18 ++ ietf/templates/wginfo/group_entry.txt | 4 + .../wginfo/group_entry_with_charter.txt | 48 +++ ietf/templates/wginfo/history.html | 9 +- ietf/templates/wginfo/wg-charter.txt | 47 --- ietf/templates/wginfo/wg-charterREDESIGN.txt | 47 --- ietf/templates/wginfo/wg-dir.html | 99 ------ ietf/templates/wginfo/wg_base.html | 4 +- ietf/templates/wginfo/wg_charter.html | 182 ----------- ietf/templates/wginfo/wg_documents.html | 50 ---- ietf/templates/wginfo/wg_documents.txt | 2 - ietf/templates/wginfo/wg_documents_entry.txt | 1 - ietf/templates/wginfo/wg_summary.txt | 5 - ietf/wgcharter/feeds.py | 4 +- ietf/wgcharter/mails.py | 2 +- ietf/wginfo/edit.py | 4 +- ietf/wginfo/mails.py | 6 +- ietf/wginfo/milestones.py | 2 +- ietf/wginfo/tests.py | 2 +- ietf/wginfo/urls.py | 10 +- ietf/wginfo/views.py | 283 +++++++++++------- 34 files changed, 563 insertions(+), 598 deletions(-) rename ietf/templates/wginfo/{wg-dirREDESIGN.html => active_wgs.html} (100%) create mode 100644 ietf/templates/wginfo/group_base.html create mode 100644 ietf/templates/wginfo/group_charter.html create mode 100644 ietf/templates/wginfo/group_documents.html create mode 100644 ietf/templates/wginfo/group_entry.txt create mode 100644 ietf/templates/wginfo/group_entry_with_charter.txt delete mode 100644 ietf/templates/wginfo/wg-charter.txt delete mode 100644 ietf/templates/wginfo/wg-charterREDESIGN.txt delete mode 100644 ietf/templates/wginfo/wg-dir.html delete mode 100644 ietf/templates/wginfo/wg_charter.html delete mode 100644 ietf/templates/wginfo/wg_documents.html delete mode 100644 ietf/templates/wginfo/wg_documents.txt delete mode 100644 ietf/templates/wginfo/wg_documents_entry.txt delete mode 100644 ietf/templates/wginfo/wg_summary.txt diff --git a/ietf/templates/doc/document_charter.html b/ietf/templates/doc/document_charter.html index fb23abb2c..885d8a4a4 100644 --- a/ietf/templates/doc/document_charter.html +++ b/ietf/templates/doc/document_charter.html @@ -25,7 +25,7 @@ {% if snapshot %}Snapshot of{% endif %} {% if doc.get_state_slug != "approved" %}Proposed{% endif %} Charter for "{{ group.name }}" - ({{ group.acronym }}) {{ group.type.name }} + ({{ group.acronym }}) {{ group.type.name }} diff --git a/ietf/templates/wginfo/1wg-charters-by-acronym.txt b/ietf/templates/wginfo/1wg-charters-by-acronym.txt index 247e1466c..bad8939cd 100644 --- a/ietf/templates/wginfo/1wg-charters-by-acronym.txt +++ b/ietf/templates/wginfo/1wg-charters-by-acronym.txt @@ -1,4 +1,4 @@ -{% load ietf_filters %}{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{{ wg }} -{% endif %}{% endif %}{% endfor %} -{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{% include "wginfo/wg-charter.txt" %} -{% endif %}{% endif %}{% endfor %} +{% autoescape off %}{% load ietf_filters %}{% for group in groups %}{{ group.acronym }} +{% endfor %} + +{% for group in groups %}{% include "wginfo/group_entry_with_charter.txt" %}{% endfor %}{% endautoescape %} diff --git a/ietf/templates/wginfo/1wg-charters.txt b/ietf/templates/wginfo/1wg-charters.txt index 4e5e70941..767dd1bb0 100644 --- a/ietf/templates/wginfo/1wg-charters.txt +++ b/ietf/templates/wginfo/1wg-charters.txt @@ -1,7 +1,6 @@ -{% load ietf_filters %}{% regroup wg_list|dictsort:"area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.name" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{{ wg }} -{% endif %}{% endif %}{% endfor %}{% endfor %} +{% autoescape off %}{% load ietf_filters %}{% for area in areas %}{% for group in area.groups %}{{ group.acronym }} +{% endfor %}{% endfor %} + +{% for area in areas %}{% for group in area.groups %}{% include "wginfo/group_entry_with_charter.txt" %}{% endfor %}{% endfor %}{% endautoescape %} -{% regroup wg_list|dictsort:"area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.name" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %} -{% include "wginfo/wg-charter.txt" %} -{% endif %}{% endif %}{% endfor %}{% endfor %} diff --git a/ietf/templates/wginfo/1wg-summary-by-acronym.txt b/ietf/templates/wginfo/1wg-summary-by-acronym.txt index d1a0f46be..028d5fd0e 100644 --- a/ietf/templates/wginfo/1wg-summary-by-acronym.txt +++ b/ietf/templates/wginfo/1wg-summary-by-acronym.txt @@ -1,10 +1,10 @@ -{% load ietf_filters %} +{% autoescape off %}{% load ietf_filters %} IETF Working Group Summary (By Acronym) The following Area Abbreviations are used in this document -{% for area in area_list %} -{{ area|upper }} - {{ area.area_acronym.name }}{% endfor %} -{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.start_date %} -{{ wg.group_acronym.name|safe }} ({{ wg }}) -- {{ wg.area.area|upper }} -{% include "wginfo/wg_summary.txt" %}{% endif %}{% endfor %} +{% for area in areas %} +{{ area.acronym|upper }} - {{ area.name }}{% endfor %} +{% for group in groups %} +{{ group.name }} ({{ group.acronym }}) -- {{ group.parent.acronym|upper }} +{% include "wginfo/group_entry.txt" %}{% endfor %}{% endautoescape %} diff --git a/ietf/templates/wginfo/1wg-summary.txt b/ietf/templates/wginfo/1wg-summary.txt index 55d76d1e2..8073c03c7 100644 --- a/ietf/templates/wginfo/1wg-summary.txt +++ b/ietf/templates/wginfo/1wg-summary.txt @@ -1,8 +1,11 @@ -{% load ietf_filters %} IETF Working Group Summary (By Area) -{% regroup wg_list|dictsort:"area.area.area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.acronym" %}{% ifequal wg.area.area.status_id 1 %}{% if forloop.first %} -{{ wg.area_acronym.name }} ({{ wg.area_acronym }}) -{{ wg.area_acronym.name|dashify }}------{% for ad in wg.area_directors %} - {{ ad.person }} <{{ ad.person.email.1 }}>{% endfor %} -{% endif %}{% if wg.start_date %} -{{ wg.group_acronym.name|safe }} ({{ wg }}) -{% include "wginfo/wg_summary.txt" %}{% endif %}{% endifequal %}{% endfor %}{% endfor %} +{% autoescape off %}{% load ietf_filters %} + IETF Working Group Summary (By Area) + + +{% for area in areas %}{{ area.name }} ({{ area.acronym }}) +{{ area.name|dashify }}------{% for ad in area.ads %} + {{ ad.person }} <{{ ad.address }}>{% endfor %} + +{% for group in area.groups %}{{ group.name }} ({{ group.acronym }}) +{% include "wginfo/group_entry.txt" %} +{% endfor %}{% endfor %}{% endautoescape %} diff --git a/ietf/templates/wginfo/wg-dirREDESIGN.html b/ietf/templates/wginfo/active_wgs.html similarity index 100% rename from ietf/templates/wginfo/wg-dirREDESIGN.html rename to ietf/templates/wginfo/active_wgs.html diff --git a/ietf/templates/wginfo/bofs.html b/ietf/templates/wginfo/bofs.html index 60df23342..3d2d5134a 100644 --- a/ietf/templates/wginfo/bofs.html +++ b/ietf/templates/wginfo/bofs.html @@ -25,7 +25,7 @@ {% for g in groups %} diff --git a/ietf/templates/wginfo/edit.html b/ietf/templates/wginfo/edit.html index ad77789a9..2ca32f710 100644 --- a/ietf/templates/wginfo/edit.html +++ b/ietf/templates/wginfo/edit.html @@ -66,7 +66,7 @@ Create new WG or BoF
- {{ g.acronym }} + {{ g.acronym }} {{ g.name }} diff --git a/ietf/templates/wginfo/chartering_wgs.html b/ietf/templates/wginfo/chartering_wgs.html index 291daa6a5..67255ca0b 100644 --- a/ietf/templates/wginfo/chartering_wgs.html +++ b/ietf/templates/wginfo/chartering_wgs.html @@ -28,7 +28,7 @@ {% for g in groups %}
- {{ g.acronym }} + {{ g.acronym }} {{ g.name }} diff --git a/ietf/templates/wginfo/conclude.html b/ietf/templates/wginfo/conclude.html index d233057c1..76391dcef 100644 --- a/ietf/templates/wginfo/conclude.html +++ b/ietf/templates/wginfo/conclude.html @@ -28,7 +28,7 @@ form.conclude .actions { {{ form.as_table }}
- Back + Back
{% ifequal action "edit" %} - Back + Back {% else %} {% ifequal action "charter" %} diff --git a/ietf/templates/wginfo/edit_milestones.html b/ietf/templates/wginfo/edit_milestones.html index ccbf0b3dd..e3a3a8a12 100644 --- a/ietf/templates/wginfo/edit_milestones.html +++ b/ietf/templates/wginfo/edit_milestones.html @@ -42,7 +42,7 @@ tr.milestone.add { font-style: italic; }

Links: - {{ group.acronym }} {{ group.type.name }} + {{ group.acronym }} {{ group.type.name }} - {{ group.charter.canonical_name }}

@@ -91,7 +91,7 @@ this list to the milestones currently in use for the {{ group.acronym }} {{
- Cancel + Cancel
diff --git a/ietf/templates/wginfo/group_base.html b/ietf/templates/wginfo/group_base.html new file mode 100644 index 000000000..bd5f47138 --- /dev/null +++ b/ietf/templates/wginfo/group_base.html @@ -0,0 +1,99 @@ +{% extends "base.html" %} +{% comment %} +Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +All rights reserved. Contact: Pasi Eronen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the Nokia Corporation and/or its + subsidiary(-ies) nor the names of its contributors may be used + to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +{% endcomment %} +{% load ietf_filters wgchairs_tags %} +{% block title %}{{ group.name }} ({{ group.acronym }}) - {% block group_subtitle %}{% endblock %}{% endblock %} + +{% block morecss %} +.ietf-navset { + background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px; + color:white; + border:1px solid black; + padding:4px; +} +.ietf-navset .selected { font-weight:bold; padding: 0 3px; } +.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } + +.ietf-group-details { float:right; padding: 4px;margin-top:16px; margin-left: 16px; } +.ietf-group-details tr { vertical-align: top; } +.ietf-concluded-bg {background-color: #F8F8D0; } +.ietf-concluded-warning { background:red;color:white;padding:2px 2px;} +.ietf-proposed-bg { } +.ietf-proposed-warning { background:green;color:white;padding:2px 2px;} +.ietf-box th { + font-weight: bold; + padding-top: 1em; + text-align: left; +} +.ietf-box tr:first-child th { + padding-top: 0; +} +{% endblock morecss %} + +{% block content %} +
+ +

{{ group.name}} ({{ group.acronym }}) + {% if group.state_id == "dormant" or group.state_id == "conclude" %}
(concluded {{ group.type.name }}){% endif %} + {% if group.state_id == "proposed" %}
(proposed {{ group.type.name }}){% endif %} +

+ +
+ Documents | + Charter | + + {% if can_manage_workflow %} + Manage workflow | + {% endif %} + + {% if can_manage_delegates %} + Manage delegations | + {% endif %} + + {% if can_manage_shepherds %} + Manage shepherds | + {% endif %} + + History | + {% if group.list_archive|startswith:"http:" or group.list_archive|startswith:"https:" or group.list_archive|startswith:"ftp:" %} + List Archive » | + {% endif %} + Tools WG Page » +
+ +{% block group_content %} +{% endblock group_content %} + +
+{% endblock content %} diff --git a/ietf/templates/wginfo/group_charter.html b/ietf/templates/wginfo/group_charter.html new file mode 100644 index 000000000..fb4d448b1 --- /dev/null +++ b/ietf/templates/wginfo/group_charter.html @@ -0,0 +1,171 @@ +{% extends "wginfo/group_base.html" %} + +{% load ietf_filters %} +{% block group_subtitle %}Charter{% endblock %} + +{% block morecss %} +{{ block.super }} +h2 a.button { margin-left: 0.5em; font-size: 13px; } +{% endblock %} + +{% block group_content %} +
+ +{% if group.state_id == "conclude" %} +Note: The data for concluded WGs +is occasionally incorrect. +{% endif %} + + + + + + + + + + + + {% if group.parent %} + + {% endif %} + + + + + + + + + + + + + + + + + + + + + + + {% if group.techadvisors %} + + + + + {% endif %} + + {% if group.editors %} + + + + + {% endif %} + + {% if group.secretaries %} + + + + + {% endif %} + + + + + + + + {% if group.state_id != "conclude" %} + + + + + + + + + + + + {% endif %} + +
Group
Name:{{ group.name }}
Acronym:{{ group.acronym }}
{{ group.parent.type.name }}:{{ group.parent.name }} ({{ group.parent.acronym }})
State:{{ group.state.name }} + {% if requested_close %} + (but in the process of being closed) + {% endif %} +
Charter: + {% if group.charter %} + {{ group.charter.name }}-{{ group.charter.rev }} ({{ group.charter.get_state.name }}) + {% else %} + none + {% if user|has_role:"Area Director,Secretariat" %} + - Submit Charter + {% endif %} + {% endif %} +
Personnel
Chair{{ group.chairs|pluralize }}: + {% for chair in group.chairs %} + {{ chair.person.plain_name }} <{{ chair.address }}>
+ {% endfor %} +
Area Director: + {% if group.areadirector %} + {{ group.areadirector.person.plain_name }} <{{ group.areadirector.address }}> + {% else %}?{% endif %} +
Tech Advisor{{ group.techadvisors|pluralize }}: + {% for techadvisor in group.techadvisors %} + {{ techadvisor.person.plain_name }} <{{ techadvisor.address }}>
+ {% endfor %} +
Editor{{ group.editors|pluralize }}: + {% for editor in group.editors %} + {{ editor.person.plain_name }} <{{ editor.address }}>
+ {% endfor %} +
Secretar{{ group.secretaries|pluralize:"y,ies" }}: + {% for secretary in group.secretaries %} + {{ secretary.person.plain_name }} <{{ secretary.address }}>
+ {% endfor %} +
Mailing List
Address:{{ group.list_email|urlize }}
To Subscribe:{{ group.list_subscribe|urlize }}
Archive:{{ group.list_archive|urlize }}
Jabber Chat
Room Address:xmpp:{{ group.acronym }}@jabber.ietf.org
Logs:http://jabber.ietf.org/logs/{{ group.acronym }}/
+ +{% if user|has_role:"Area Director,Secretariat" %} +
+ {% for name, url in actions %} + {{ name }} + {% endfor %} +
+{% endif %} +
+ +{% with group.groupurl_set.all as urls %} +{% if urls %} +

In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at: +{% for url in urls %} +{{ url.name }}{% if not forloop.last %}, {% endif %} +{% endfor %} +

+{% endif %} +{% endwith %} + +

Charter for {% if group.state_id == "proposed" %}Proposed{% endif %} Working Group

+ +

{{ group.charter_text|escape|format_charter|safe }}

+ +

{% if group.state_id == "proposed" %}Proposed{% endif %} Milestones + +{% if group.state_id != "proposed" %} +{% if user|has_role:"Area Director,Secretariat" or is_chair %} +Add or edit milestones +{% endif %} +{% endif %} +

+ +{% with group.milestones as milestones %} +{% include "wginfo/milestones.html" %} +{% endwith %} + +{% if milestones_in_review %} +

+ {{ milestones_in_review|length }} new milestone{{ milestones_in_review|pluralize }} +currently in Area Director review.

+{% endif %} + +{% endblock %} diff --git a/ietf/templates/wginfo/group_documents.html b/ietf/templates/wginfo/group_documents.html new file mode 100644 index 000000000..a1adc68ef --- /dev/null +++ b/ietf/templates/wginfo/group_documents.html @@ -0,0 +1,18 @@ +{% extends "wginfo/group_base.html" %} + +{% block group_subtitle %}Documents{% endblock %} + +{% block group_content %} +
+ +{% include "doc/search/search_results.html" %} + +{% with docs_related as docs %}{% with meta_related as meta %}{% include "doc/search/search_results.html" %}{% endwith %}{% endwith %} + +
+{% endblock group_content %} + +{% block js %} + + +{% endblock %} diff --git a/ietf/templates/wginfo/group_entry.txt b/ietf/templates/wginfo/group_entry.txt new file mode 100644 index 000000000..c58fca075 --- /dev/null +++ b/ietf/templates/wginfo/group_entry.txt @@ -0,0 +1,4 @@ +{% for chair in group.chairs %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person.plain_name }} <{{ chair.address }}> +{% endfor %} WG Mail: {{ group.list_email }} + To Join: {{ group.list_subscribe }} + Archive: {{ group.list_archive }} diff --git a/ietf/templates/wginfo/group_entry_with_charter.txt b/ietf/templates/wginfo/group_entry_with_charter.txt new file mode 100644 index 000000000..fb904032a --- /dev/null +++ b/ietf/templates/wginfo/group_entry_with_charter.txt @@ -0,0 +1,48 @@ +{% autoescape off %}{% load ietf_filters %}{{ group.name }} ({{group.acronym}}) +{{ group.name|dashify }}{{ group.acronym|dashify }}--- + + Charter + Last Modified: {{ group.time.date|date }} + + Current Status: {{ group.state.name }} + + Chair{{ group.chairs|pluralize }}: +{% for chair in group.chairs %} {{ chair.person.name }} <{{chair.address}}> +{% endfor %} + {{ group.area.name}} Directors: +{% for ad in group.area.ads %} {{ ad.person.plain_name }} <{{ ad }}> +{% endfor %} +{% if group.areadirector %} {{ group.area.name }} Advisor: + {{ group.areadirector.person.plain_name }} <{{ group.areadirector.address }}> +{% endif %}{% if group.techadvisors %} + Tech Advisor{{ group.techadvisors|pluralize }}: +{% for techadvisor in group.techadvisors %} {{ techadvisor.person.plain_name }} <{{ techadvisor.address }}> +{% endfor %}{% endif %}{% if group.editors %} + Editor{{ group.editors|pluralize }}: +{% for editor in group.editors %} {{ editor.person.plain_name }} <{{ editor.address}}> +{% endfor %}{% endif %}{% if group.secretaries %} + Secretar{{ group.secretaries|pluralize:"y,ies" }}: +{% for secretary in group.secretaries %} {{ secretary.person.plain_name }} <{{ secretary.address }}> +{% endfor %}{% endif %} + Mailing Lists: + General Discussion: {{ group.list_email }} + To Subscribe: {{ group.list_subscribe }} + Archive: {{ group.list_archive }} + +Description of Working Group: + + {{ group.charter_text|indent }} + +Goals and Milestones: +{% for milestone in group.milestones %} {% if milestone.resolved %}{{ milestone.resolved }} {% else %}{{ milestone.due|date:"M Y" }}{% endif %} - {{ milestone.desc }} +{% endfor %} +Internet-Drafts: +{% for alias in group.drafts %} - {{ alias.document.title }} [{{ alias.name }}-{{ alias.document.rev }}] ({{ alias.document.pages }} pages) +{% endfor %} +{% if group.rfcs %}Requests for Comments: +{% for alias in group.rfcs %} {{ alias.name.upper }}: {{ alias.document.title}} ({{ alias.document.pages }} pages){% for r in alias.rel %} + * {{ r.action }} {{ r.target.name|upper }}{% endfor %}{% for r in alias.invrel %} + * {% if r.relationsship == "obs" %}{{ r.inverse_action|upper }}{% else %}{{ r.action }}{% endif %} {{ r.source.canonical_name|upper }}{% endfor %} +{% endfor %} +{% else %}No Requests for Comments{% endif %} +{% endautoescape %} diff --git a/ietf/templates/wginfo/history.html b/ietf/templates/wginfo/history.html index ed86d4324..bc1926609 100644 --- a/ietf/templates/wginfo/history.html +++ b/ietf/templates/wginfo/history.html @@ -1,15 +1,16 @@ -{% extends "wginfo/wg_base.html" %} +{% extends "wginfo/group_base.html" %} {% load ietf_filters %} -{% block wg_titledetail %}History{% endblock %} +{% block group_subtitle %}History{% endblock %} -{% block wg_content %} +{% block group_content %} {% load ietf_filters %} -

WG History

+

Group History

+ {% for e in events %} diff --git a/ietf/templates/wginfo/wg-charter.txt b/ietf/templates/wginfo/wg-charter.txt deleted file mode 100644 index c0d8af0d4..000000000 --- a/ietf/templates/wginfo/wg-charter.txt +++ /dev/null @@ -1,47 +0,0 @@ -{% if USE_DB_REDESIGN_PROXY_CLASSES %}{% include "wginfo/wg-charterREDESIGN.txt" %}{% else %}{% load ietf_filters %}{{wg.group_acronym.name|safe}} ({{wg}}) -{{ wg.group_acronym.name|dashify }}{{ wg.group_acronym.acronym|dashify }}--- - - Charter - Last Modified: {{ wg.last_modified_date }} - - Current Status: {{ wg.status }} - - Chair{{ wg.chairs.count|pluralize:",s" }}: -{% for chair in wg.chairs %} {{ chair.person|safe }} <{{chair.person.email.1}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Directors: -{% for ad in wg.area_directors %} {{ ad.person|safe }} <{{ad.person.email.1}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Advisor: - {{ wg.area_director.person|safe }} <{{wg.area_director.person.email.1}}> -{% if wg.wgtechadvisor_set.count %} - Tech Advisor{{ wg.wgtechadvisor_set.count|pluralize:",s" }}: -{% for techadvisor in wg.wgtechadvisor_set.all %} {{ techadvisor.person|safe }} <{{techadvisor.person.email.1}}> -{% endfor %}{% endif %}{% if wg.wgeditor_set.count %} - Editor{{ wg.wgeditor_set.count|pluralize:",s" }}: -{% for editor in wg.wgeditor_set.all %} {{ editor.person|safe }} <{{editor.person.email.1}}> -{% endfor %}{% endif %}{% if wg.secretaries %} - Secretar{{ wg.secretaries.count|pluralize:"y,ies" }}: -{% for secretary in wg.secretaries %} {{ secretary.person|safe }} <{{secretary.person.email.1}}> -{% endfor %}{% endif %} - Mailing Lists: - General Discussion: {{ wg.email_address }} - To Subscribe: {{ wg.email_subscribe }} - Archive: {{ wg.email_archive }} - -Description of Working Group: - - {{ wg.charter_text|indent|safe }} - -Goals and Milestones: -{% for milestone in wg.milestones %} {% ifequal milestone.done 'Done' %}Done {% else %}{%ifequal milestone.expected_due_date.month 1 %}Jan{% endifequal %}{%ifequal milestone.expected_due_date.month 2 %}Feb{% endifequal %}{%ifequal milestone.expected_due_date.month 3 %}Mar{% endifequal %}{%ifequal milestone.expected_due_date.month 4 %}Apr{% endifequal %}{%ifequal milestone.expected_due_date.month 5 %}May{% endifequal %}{%ifequal milestone.expected_due_date.month 6 %}Jun{% endifequal %}{%ifequal milestone.expected_due_date.month 7 %}Jul{% endifequal %}{%ifequal milestone.expected_due_date.month 8 %}Aug{% endifequal %}{%ifequal milestone.expected_due_date.month 9 %}Sep{% endifequal %}{%ifequal milestone.expected_due_date.month 10 %}Oct{% endifequal %}{%ifequal milestone.expected_due_date.month 11 %}Nov{% endifequal %}{%ifequal milestone.expected_due_date.month 12 %}Dec{% endifequal %} {{ milestone.expected_due_date.year }}{% endifequal %} - {{ milestone.description|safe }} -{% endfor %} -Internet-Drafts: -{% for draft in wg.drafts %} - {{draft.title|safe}} [{{draft.filename}}-{{draft.revision}}] ({{ draft.txt_page_count }} pages) -{% endfor %} -{% if wg.rfcs %}Requests for Comments: -{% for rfc in wg.rfcs %} {{rfc}}: {{rfc.title|safe}} ({{ rfc.txt_page_count }} pages){% for obs in rfc.obsoletes%} - * {{obs.action}} RFC{{obs.rfc_acted_on_id}}{% endfor %}{% for obs in rfc.obsoleted_by%} - * {%ifequal obs.action 'Obsoletes'%}OBSOLETED BY{%else%}Updated by{%endifequal%} RFC{{obs.rfc_id}}{% endfor %} -{%endfor%} -{%else%}No Requests for Comments{% endif %}{% endif %} diff --git a/ietf/templates/wginfo/wg-charterREDESIGN.txt b/ietf/templates/wginfo/wg-charterREDESIGN.txt deleted file mode 100644 index a46bb0b08..000000000 --- a/ietf/templates/wginfo/wg-charterREDESIGN.txt +++ /dev/null @@ -1,47 +0,0 @@ -{% load ietf_filters %}{{wg.name|safe}} ({{wg.acronym}}) -{{ wg.name|dashify }}{{ wg.acronym|dashify }}--- - - Charter - Last Modified: {{ wg.time.date }} - - Current Status: {{ wg.state.name }} - - Chair{{ wg.chairs|pluralize }}: -{% for chair in wg.chairs %} {{ chair.person.name|safe }} <{{chair.address}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Directors: -{% for ad in wg.area_directors %} {{ ad.person|safe }} <{{ad.person.email.1}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Advisor: - {{ wg.areadirector.person.name|safe }} <{{wg.areadirector.address}}> -{% if wg.techadvisors %} - Tech Advisor{{ wg.techadvisors|pluralize }}: -{% for techadvisor in wg.techadvisors %} {{ techadvisor.person.plain_name|safe }} <{{techadvisor.address}}> -{% endfor %}{% endif %}{% if wg.editors %} - Editor{{ wg.editors|pluralize }}: -{% for editor in wg.editors %} {{ editor.person.plain_name|safe }} <{{editor.person.address}}> -{% endfor %}{% endif %}{% if wg.secretaries %} - Secretar{{ wg.secretaries|pluralize:"y,ies" }}: -{% for secretary in wg.secretaries %} {{ secretary.person.plain_name|safe }} <{{secretary.person.address}}> -{% endfor %}{% endif %} - Mailing Lists: - General Discussion: {{ wg.email_address }} - To Subscribe: {{ wg.email_subscribe }} - Archive: {{ wg.email_archive }} - -Description of Working Group: - - {{ wg.charter_text|indent|safe }} - -Goals and Milestones: -{% for milestone in wg.milestones %} {% if milestone.resolved %}{{ milestone.resolved }} {% else %}{{ milestone.due|date:"M Y" }}{% endif %} - {{ milestone.desc|safe }} -{% endfor %} -Internet-Drafts: -{% for alias in wg.drafts %} - {{alias.document.title|safe}} [{{alias.name}}-{{alias.document.rev}}] ({{ alias.document.pages }} pages) -{% endfor %} -{% if wg.rfcs %}Requests for Comments: -{% for alias in wg.rfcs %} {{ alias.name.upper }}: {{ alias.document.title|safe}} ({{ alias.document.pages }} pages){% for r in alias.rel %} - * {{ r.action }} {{ r.target.name|upper }}{% endfor %}{% for r in alias.invrel %} - * {% ifequal r.relationsship "obs" %}{{ r.inverse_action|upper }}{% else %}{{ r.action }}{% endifequal %} {{ r.source.canonical_name|upper }}{% endfor %} -{%endfor%} -{%else%}No Requests for Comments{% endif %} diff --git a/ietf/templates/wginfo/wg-dir.html b/ietf/templates/wginfo/wg-dir.html deleted file mode 100644 index b9cfb208b..000000000 --- a/ietf/templates/wginfo/wg-dir.html +++ /dev/null @@ -1,99 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2009, All Rights Reserved #} -{% comment %} -Portion Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} - -{% block title %}Active IETF Working Groups{% endblock %} - -{% block morecss %} -.ietf-wg-table { width: 100%; max-width:50em; } -.ietf-wg-table tr { vertical-align:top; } -{% endblock morecss %} - -{% block content %} -

Active IETF Working Groups

- -

See also: - Concluded Working Groups (www.ietf.org), - Concluded Working Groups (tools.ietf.org), - Historic Charters. -

- - {% for area in areas|dictsort:"area_acronym.name" %} -

{{ area.area_acronym.name }}

- - {% for ad in area.areadirector_set.all|dictsort:"person.last_name" %} - {% if forloop.first %} -

Area Director{{ forloop.revcounter|pluralize }}:

-
DateByText
{{ e.time|date:"Y-m-d"}}
- {% endif %} - - {% if forloop.last %} -
  {{ ad.person }} <{{ ad.person.email.1 }}>
- {% endif %} - {% endfor %} - - {% for url in area.additional_urls %} - {% if forloop.first %} -

Area Specific Web Page{{ forloop.revcounter|pluralize}}:

-

- {% endif %} - {{ url.description }}{% if not forloop.last %}
{% endif %} - {% if forloop.last %} -

- {% endif %} - {% endfor %} - - {% for wg in area.active_wgs %} - {% if forloop.first %} -

Active Working Groups:

-
- - {% endif %} - - - - - - - {% if forloop.last %} -
{{ wg }}{% for ad in area.areadirector_set.all|dictsort:"person.last_name" %}{% ifequal ad wg.area_director %}{% endifequal %}{% endfor %}{{ wg.group_acronym.name }}{% for chair in wg.chairs %}{{chair.person}}{% if not forloop.last %}, {% endif %}{% endfor %}
-
- {% endif %} - {% empty %} -

No Active Working Groups

- {% endfor %}{# wg #} - - {% endfor %}{# area #} -{% endblock %} diff --git a/ietf/templates/wginfo/wg_base.html b/ietf/templates/wginfo/wg_base.html index e4a14be8e..4f73e3fe4 100644 --- a/ietf/templates/wginfo/wg_base.html +++ b/ietf/templates/wginfo/wg_base.html @@ -66,8 +66,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}){% if concluded %}
(concluded WG){% endif %}{% if proposed %}
(proposed WG){% endif %}

-{% ifequal selected "documents" %}Documents{% else %}Documents{% endifequal %} | -{% ifequal selected "charter" %}Charter{% else %}Charter{% endifequal %} | +{% ifequal selected "documents" %}Documents{% else %}Documents{% endifequal %} | +{% ifequal selected "charter" %}Charter{% else %}Charter{% endifequal %} | {% wgchairs_admin_options wg %} History | {% if wg.clean_email_archive|startswith:"http:" or wg.clean_email_archive|startswith:"https:" or wg.clean_email_archive|startswith:"ftp:" %} diff --git a/ietf/templates/wginfo/wg_charter.html b/ietf/templates/wginfo/wg_charter.html deleted file mode 100644 index e4797950b..000000000 --- a/ietf/templates/wginfo/wg_charter.html +++ /dev/null @@ -1,182 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% comment %} -Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% load ietf_filters %} -{% block wg_titledetail %}Charter{% endblock %} - -{% block morecss %} -{{ block.super }} -h2 a.button { margin-left: 0.5em; font-size: 13px; } -{% endblock %} - -{% block wg_content %} -
-{% if concluded %} -Note: The data for concluded WGs -is occasionally incorrect. -{% endif %} - - - - - - - - - - - {% if wg.parent %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - {% if wg.techadvisors %} - - - - - {% endif %} - {% if wg.editors %} - - - {% endif %} - {% if wg.secretaries %} - - - - - {% endif %} - - - - - - - - {% if not concluded %} - - - - - {% endif %} - -
Group
Name:{{ wg.name }}
Acronym:{{ wg.acronym }}
Area:{{ wg.parent.name }} ({{ wg.parent.acronym }})
State:{{ wg.state.name }} - {% if requested_close %} - (but in the process of being closed) - {% endif %} -
Charter: - {% if wg.charter %} - {{ wg.charter.name }}-{{ wg.charter.rev }} ({{ wg.charter.get_state.name }}) - {% else %} - none - {% if user|has_role:"Area Director,Secretariat" %} - - Submit Charter - {% endif %} - {% endif %} -
Personnel
Chair{{ wg.chairs|pluralize }}: - {% for chair in wg.chairs %} - {{ chair.person.plain_name }} <{{ chair.address }}>
- {% endfor %} -
Area Director: - {% if not wg.ad %}?{% else %} - {{ wg.ad.plain_name }} <{{ wg.areadirector.address }}>{% endif %} -
Tech Advisor{{ wg.techadvisors|pluralize }}: - {% for techadvisor in wg.techadvisors %} - {{ techadvisor.person.plain_name }} <{{ techadvisor.address }}>
- {% endfor %} -
Editor{{ wg.editors|pluralize }}: - {% for editor in wg.editors %} - {{ editor.person.plain_name }} <{{ editor.address }}>
- {% endfor %} -
Secretar{{ wg.secretaries|pluralize:"y,ies" }}: - {% for secretary in wg.secretaries %} - {{ secretary.person.plain_name }} <{{ secretary.address }}>
- {% endfor %} -
Mailing List
Address:{{ wg.email_address|urlize }}
To Subscribe:{{ wg.email_subscribe|urlize }}
Archive:{{ wg.clean_email_archive|urlize }}
Jabber Chat
Room Address:xmpp:{{ wg.acronym }}@jabber.ietf.org
Logs:http://jabber.ietf.org/logs/{{ wg.acronym }}/
- -{% if user|has_role:"Area Director,Secretariat" %} -
- {% for name, url in actions %} - {{ name }} - {% if not forloop.last %}|{% endif %} - {% endfor %} -
-{% endif %} -
- -{% if wg.additional_urls %} -

In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at: -{% for url in wg.additional_urls %} -{{ url.name }}{% if not forloop.last %}, {% endif %} -{% endfor %} -

-{% endif %} - -

Charter for {% if wg.state_id == "proposed" %}Proposed{% endif %} Working Group

-

{{ wg.charter_text|escape|format_charter|safe }}

- -

{% if wg.state_id == "proposed" %}Proposed{% endif %} Milestones -{% if wg.state_id != "proposed" %} -{% if user|has_role:"Area Director,Secretariat" or is_chair %} -Add or edit milestones -{% endif %} -{% endif %} -

- -{% with wg.milestones as milestones %}{% include "wginfo/milestones.html" %}{% endwith %} - -{% if milestones_in_review %} -

+ {{ milestones_in_review|length }} new milestone{{ milestones_in_review|pluralize }} -currently in Area Director review.

-{% endif %} -{% endblock wg_content %} diff --git a/ietf/templates/wginfo/wg_documents.html b/ietf/templates/wginfo/wg_documents.html deleted file mode 100644 index f0fb4529b..000000000 --- a/ietf/templates/wginfo/wg_documents.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% comment %} -Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% block wg_titledetail %}Documents{% endblock %} - -{% block wg_content %} -
- -{% include "doc/search/search_results.html" %} - -{% with docs_related as docs %}{% include "doc/search/search_results.html" %}{% endwith %} - -
-{% endblock wg_content %} - -{% block js %} - - -{% endblock %} diff --git a/ietf/templates/wginfo/wg_documents.txt b/ietf/templates/wginfo/wg_documents.txt deleted file mode 100644 index 3b46a8249..000000000 --- a/ietf/templates/wginfo/wg_documents.txt +++ /dev/null @@ -1,2 +0,0 @@ -{% load ietf_filters %}{% regroup docs by view_sort_group as grouped_docs %}{% for doc_group in grouped_docs %}{% for doc in doc_group.list %}{% include "wginfo/wg_documents_entry.txt" %}{% endfor %}{% endfor %}{% regroup docs_related by view_sort_group as grouped_docs_related %}{% for doc_group in grouped_docs_related %}{% for doc in doc_group.list %}Related {% include "wginfo/wg_documents_entry.txt" %}{% endfor %}{% endfor %} - diff --git a/ietf/templates/wginfo/wg_documents_entry.txt b/ietf/templates/wginfo/wg_documents_entry.txt deleted file mode 100644 index 19f456c33..000000000 --- a/ietf/templates/wginfo/wg_documents_entry.txt +++ /dev/null @@ -1 +0,0 @@ -{% load ietf_filters %}{{doc_group.grouper}} {% if doc.rfc %}{{doc.rfc.rfc_number}} {{doc.rfc.title|clean_whitespace}}{% else %}{{doc.id.draft_name_and_revision}} {{doc.id.title|clean_whitespace}}{% endif %} diff --git a/ietf/templates/wginfo/wg_summary.txt b/ietf/templates/wginfo/wg_summary.txt deleted file mode 100644 index 603d9e2f8..000000000 --- a/ietf/templates/wginfo/wg_summary.txt +++ /dev/null @@ -1,5 +0,0 @@ -{% for chair in wg.wgchair_set.all %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person|safe }} <{{ chair.person.email.1 }}> -{% endfor %} WG Mail: {{ wg.email_address }} - To Join: {{ wg.email_subscribe }}{%if wg.email_keyword %} - In Body: {{ wg.email_keyword|safe }}{% endif %} - Archive: {{ wg.email_archive }} diff --git a/ietf/wgcharter/feeds.py b/ietf/wgcharter/feeds.py index 5abf7708c..1dd8bbfff 100644 --- a/ietf/wgcharter/feeds.py +++ b/ietf/wgcharter/feeds.py @@ -26,7 +26,7 @@ class GroupChanges(Feed): def link(self, obj): if not obj: raise FeedDoesNotExist - return urlreverse('wg_charter', kwargs={'acronym': obj.acronym}) + return urlreverse('group_charter', kwargs={'acronym': obj.acronym}) def description(self, obj): return self.title(obj) @@ -44,7 +44,7 @@ class GroupChanges(Feed): if isinstance(obj, DocEvent): return urlreverse("doc_view", kwargs={'name': obj.doc_id }) elif isinstance(obj, GroupEvent): - return urlreverse('wg_charter', kwargs={'acronym': obj.group.acronym }) + return urlreverse('group_charter', kwargs={'acronym': obj.group.acronym }) def item_pubdate(self, obj): return obj.time diff --git a/ietf/wgcharter/mails.py b/ietf/wgcharter/mails.py index 29c603b4e..0f983a017 100644 --- a/ietf/wgcharter/mails.py +++ b/ietf/wgcharter/mails.py @@ -33,7 +33,7 @@ def email_secretariat(request, group, type, text): "wgcharter/email_secretariat.txt", dict(text=text, group=group, - group_url=settings.IDTRACKER_BASE_URL + urlreverse('wg_charter', kwargs=dict(acronym=group.acronym)), + group_url=settings.IDTRACKER_BASE_URL + urlreverse('group_charter', kwargs=dict(acronym=group.acronym)), charter_url=settings.IDTRACKER_BASE_URL + urlreverse('doc_view', kwargs=dict(name=group.charter.name)), ) ) diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index 945129ff7..670d876c5 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -258,7 +258,7 @@ def edit(request, acronym=None, action="edit"): if action=="charter": return redirect('charter_submit', name=wg.charter.name, option="initcharter") - return redirect('wg_charter', acronym=wg.acronym) + return redirect('group_charter', acronym=wg.acronym) else: # form.is_valid() if not new_wg: from ietf.person.forms import json_emails @@ -312,7 +312,7 @@ def conclude(request, acronym): e.desc = "Requested closing group" e.save() - return redirect('wg_charter', acronym=wg.acronym) + return redirect('group_charter', acronym=wg.acronym) else: form = ConcludeForm() diff --git a/ietf/wginfo/mails.py b/ietf/wginfo/mails.py index 96b8bb6e8..02606d30e 100644 --- a/ietf/wginfo/mails.py +++ b/ietf/wginfo/mails.py @@ -16,7 +16,7 @@ def email_milestones_changed(request, group, changes): def wrap_up_email(to, text): text = wrap(strip_tags(text), 70) text += "\n\n" - text += u"URL: %s" % (settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym))) + text += u"URL: %s" % (settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym))) send_mail_text(request, to, None, u"Milestones changed for %s %s" % (group.acronym, group.type.name), @@ -95,7 +95,7 @@ def email_milestones_due(group, early_warning_days): milestones=milestones, today=today, early_warning_days=early_warning_days, - url=settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym)) + url=settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym)) )) def groups_needing_milestones_due_reminder(early_warning_days): @@ -120,7 +120,7 @@ def email_milestones_overdue(group): "wginfo/reminder_milestones_overdue.txt", dict(group=group, milestones=milestones, - url=settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym)) + url=settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym)) )) def groups_needing_milestones_overdue_reminder(grace_period=30): diff --git a/ietf/wginfo/milestones.py b/ietf/wginfo/milestones.py index 3e7b3df64..dee1765cc 100644 --- a/ietf/wginfo/milestones.py +++ b/ietf/wginfo/milestones.py @@ -313,7 +313,7 @@ def edit_milestones(request, acronym, milestone_set="current"): if milestone_set == "charter": return redirect('doc_view', name=group.charter.canonical_name()) else: - return redirect('wg_charter', acronym=group.acronym) + return redirect('group_charter', acronym=group.acronym) else: for m in milestones: forms.append(MilestoneForm(instance=m, needs_review=needs_review)) diff --git a/ietf/wginfo/tests.py b/ietf/wginfo/tests.py index 75e8db055..f50d14885 100644 --- a/ietf/wginfo/tests.py +++ b/ietf/wginfo/tests.py @@ -178,7 +178,7 @@ class WgEditTestCase(django.test.TestCase): make_test_data() group = Group.objects.get(acronym="mars") - url = urlreverse('wg_edit', kwargs=dict(acronym=group.acronym)) + url = urlreverse('group_edit', kwargs=dict(acronym=group.acronym)) login_testing_unauthorized(self, "secretary", url) # normal get diff --git a/ietf/wginfo/urls.py b/ietf/wginfo/urls.py index 80651befd..956ee2de6 100644 --- a/ietf/wginfo/urls.py +++ b/ietf/wginfo/urls.py @@ -6,7 +6,7 @@ from django.views.generic.simple import redirect_to urlpatterns = patterns('', - (r'^$', views.wg_dir), + (r'^$', views.active_wgs), (r'^summary.txt', redirect_to, { 'url':'/wg/1wg-summary.txt' }), (r'^summary-by-area.txt', redirect_to, { 'url':'/wg/1wg-summary.txt' }), (r'^summary-by-acronym.txt', redirect_to, { 'url':'/wg/1wg-summary-by-acronym.txt' }), @@ -18,12 +18,12 @@ urlpatterns = patterns('', (r'^bofs/$', views.bofs), (r'^chartering/create/$', edit.edit, {'action': "charter"}, "wg_create"), (r'^bofs/create/$', edit.edit, {'action': "create"}, "bof_create"), - (r'^(?P[a-zA-Z0-9-]+)/documents/txt/$', views.wg_documents_txt), - (r'^(?P[a-zA-Z0-9-]+)/$', views.wg_documents_html, None, "wg_docs"), - (r'^(?P[a-zA-Z0-9-]+)/charter/$', views.wg_charter, None, 'wg_charter'), + (r'^(?P[a-zA-Z0-9-]+)/documents/txt/$', views.group_documents_txt), + (r'^(?P[a-zA-Z0-9-]+)/$', views.group_documents, None, "wg_docs"), + (r'^(?P[a-zA-Z0-9-]+)/charter/$', views.group_charter, None, 'group_charter'), (r'^(?P[a-zA-Z0-9-]+)/init-charter/', edit.submit_initial_charter, None, "wg_init_charter"), (r'^(?P[a-zA-Z0-9-]+)/history/$', views.history), - (r'^(?P[a-zA-Z0-9-]+)/edit/$', edit.edit, {'action': "edit"}, "wg_edit"), + (r'^(?P[a-zA-Z0-9-]+)/edit/$', edit.edit, {'action': "edit"}, "group_edit"), (r'^(?P[a-zA-Z0-9-]+)/conclude/$', edit.conclude, None, "wg_conclude"), (r'^(?P[a-zA-Z0-9-]+)/milestones/$', milestones.edit_milestones, {'milestone_set': "current"}, "wg_edit_milestones"), (r'^(?P[a-zA-Z0-9-]+)/milestones/charter/$', milestones.edit_milestones, {'milestone_set': "charter"}, "wg_edit_charter_milestones"), diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 7af56f075..dd2070200 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -32,77 +32,111 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import itertools + from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext, loader +from django.template import RequestContext from django.http import HttpResponse from django.conf import settings from django.core.urlresolvers import reverse as urlreverse -from ietf.idtracker.models import Area, IETFWG + from ietf.doc.views_search import SearchForm, retrieve_search_results -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper from ietf.ipr.models import IprDetail -from ietf.group.models import Group -from ietf.doc.models import State +from ietf.group.models import Group, GroupURL +from ietf.doc.models import State, DocAlias, RelatedDocument from ietf.doc.utils import get_chartering_type +from ietf.person.models import Email +from ietf.group.utils import get_charter_text +from ietf.doc.templatetags.ietf_filters import clean_whitespace + +from ietf.wgchairs.accounts import (can_manage_workflow_in_group, + can_manage_delegates_in_group, + can_manage_shepherds_in_group) -def fill_in_charter_info(wg, include_drafts=False): - from ietf.person.models import Email - from ietf.doc.models import DocAlias, RelatedDocument +def fill_in_charter_info(group, include_drafts=False): + group.areadirector = group.ad.role_email("ad", group.parent) if group.ad else None + group.chairs = Email.objects.filter(role__group=group, role__name="chair") + group.techadvisors = Email.objects.filter(role__group=group, role__name="techadv") + group.editors = Email.objects.filter(role__group=group, role__name="editor") + group.secretaries = Email.objects.filter(role__group=group, role__name="secr") + milestone_state = "charter" if group.state_id == "proposed" else "active" + group.milestones = group.groupmilestone_set.filter(state=milestone_state).order_by('due') - wg.areadirector = wg.ad.role_email("ad", wg.parent) if wg.ad else None - wg.chairs = Email.objects.filter(role__group=wg, role__name="chair") - wg.techadvisors = Email.objects.filter(role__group=wg, role__name="techadv") - wg.editors = Email.objects.filter(role__group=wg, role__name="editor") - wg.secretaries = Email.objects.filter(role__group=wg, role__name="secr") - milestone_state = "charter" if wg.state_id == "proposed" else "active" - wg.milestones = wg.groupmilestone_set.filter(state=milestone_state).order_by('due') + group.charter_text = get_charter_text(group) if include_drafts: - aliases = DocAlias.objects.filter(document__type="draft", document__group=wg).select_related('document').order_by("name") - wg.drafts = [] - wg.rfcs = [] + aliases = DocAlias.objects.filter(document__type="draft", document__group=group).select_related('document').order_by("name") + group.drafts = [] + group.rfcs = [] for a in aliases: if a.name.startswith("draft"): - wg.drafts.append(a) + group.drafts.append(a) else: - wg.rfcs.append(a) + group.rfcs.append(a) a.rel = RelatedDocument.objects.filter(source=a.document).distinct() a.invrel = RelatedDocument.objects.filter(target=a).distinct() -def wg_summary_acronym(request): - areas = Area.active_areas() - wgs = IETFWG.objects.filter(status=IETFWG.ACTIVE) - return HttpResponse(loader.render_to_string('wginfo/1wg-summary-by-acronym.txt', {'area_list': areas, 'wg_list': wgs}),mimetype='text/plain; charset=UTF-8') +def extract_last_name(email): + return email.person.name_parts()[3] + +def extract_group_chairs(group): + return sorted(Email.objects.filter(role__group=group, role__name="chair").select_related("person"), key=extract_last_name) def wg_summary_area(request): - wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False).exclude(parent=None) - return HttpResponse(loader.render_to_string('wginfo/1wg-summary.txt', {'wg_list': wgs}),mimetype='text/plain; charset=UTF-8') - -def wg_charters(request): - wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False).exclude(parent=None) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - for wg in wgs: - fill_in_charter_info(wg, include_drafts=True) - return HttpResponse(loader.render_to_string('wginfo/1wg-charters.txt', {'wg_list': wgs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}),mimetype='text/plain; charset=UTF-8') - -def wg_charters_by_acronym(request): - wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - for wg in wgs: - fill_in_charter_info(wg, include_drafts=True) - return HttpResponse(loader.render_to_string('wginfo/1wg-charters-by-acronym.txt', {'wg_list': wgs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}),mimetype='text/plain; charset=UTF-8') - -def wg_dir(request): - areas = Area.active_areas() - return render_to_response('wginfo/wg-dir.html', {'areas':areas}, RequestContext(request)) - -def wg_dirREDESIGN(request): - from ietf.group.models import Group, GroupURL - from ietf.person.models import Email - areas = Group.objects.filter(type="area", state="active").order_by("name") for area in areas: + area.ads = sorted(Email.objects.filter(role__group=area, role__name="ad").select_related("person"), key=extract_last_name) + area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") + for group in area.groups: + group.chairs = extract_group_chairs(group) + + areas = [a for a in areas if a.groups] + + return render_to_response('wginfo/1wg-summary.txt', + { 'areas': areas }, + mimetype='text/plain; charset=UTF-8') + +def wg_summary_acronym(request): + areas = Group.objects.filter(type="area", state="active").order_by("name") + groups = Group.objects.filter(type="wg", state="active").order_by("acronym").select_related("parent") + for group in groups: + group.chairs = extract_group_chairs(group) + return render_to_response('wginfo/1wg-summary-by-acronym.txt', + { 'areas': areas, + 'groups': groups }, + mimetype='text/plain; charset=UTF-8') + +def wg_charters(request): + areas = Group.objects.filter(type="area", state="active").order_by("name") + for area in areas: + area.ads = sorted(Email.objects.filter(role__group=area, role__name="ad").select_related("person"), key=extract_last_name) + area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("name") + for group in area.groups: + fill_in_charter_info(group, include_drafts=True) + group.area = area + return render_to_response('wginfo/1wg-charters.txt', + { 'areas': areas }, + mimetype='text/plain; charset=UTF-8') + +def wg_charters_by_acronym(request): + areas = dict((a.id, a) for a in Group.objects.filter(type="area", state="active").order_by("name")) + + for area in areas.itervalues(): + area.ads = sorted(Email.objects.filter(role__group=area, role__name="ad").select_related("person"), key=extract_last_name) + + groups = Group.objects.filter(type="wg", state="active").exclude(parent=None).order_by("acronym") + for group in groups: + fill_in_charter_info(group, include_drafts=True) + group.area = areas.get(group.parent_id) + return render_to_response('wginfo/1wg-charters-by-acronym.txt', + { 'groups': groups }, + mimetype='text/plain; charset=UTF-8') + +def active_wgs(request): + areas = Group.objects.filter(type="area", state="active").order_by("name") + for area in areas: + # dig out information for template area.ads = [] for e in Email.objects.filter(role__group=area, role__name="ad").select_related("person"): e.incoming = False @@ -112,26 +146,22 @@ def wg_dirREDESIGN(request): e.incoming = True area.ads.append(e) - area.ads.sort(key=lambda e: (e.incoming, e.person.name_parts()[3])) + area.ads.sort(key=lambda e: (e.incoming, extract_last_name(e))) area.wgs = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") area.urls = area.groupurl_set.all().order_by("name") for wg in area.wgs: - wg.chairs = sorted(Email.objects.filter(role__group=wg, role__name="chair").select_related("person"), key=lambda e: e.person.name_parts()[3]) - - return render_to_response('wginfo/wg-dirREDESIGN.html', {'areas':areas}, RequestContext(request)) + wg.chairs = extract_group_chairs(wg) -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - wg_dir = wg_dirREDESIGN + return render_to_response('wginfo/active_wgs.html', {'areas':areas}, RequestContext(request)) def bofs(request): groups = Group.objects.filter(type="wg", state="bof") - return render_to_response('wginfo/bofs.html',dict(groups=groups),RequestContext(request)) + return render_to_response('wginfo/bofs.html',dict(groups=groups), RequestContext(request)) def chartering_wgs(request): charter_states = State.objects.filter(used=True, type="charter").exclude(slug__in=("approved", "notrev")) groups = Group.objects.filter(type="wg", charter__states__in=charter_states).select_related("state", "charter") - for g in groups: g.chartering_type = get_chartering_type(g.charter) @@ -141,31 +171,40 @@ def chartering_wgs(request): RequestContext(request)) -def wg_documents(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - concluded = wg.status_id in [ 2, 3, ] - proposed = (wg.status_id == 4) - form = SearchForm({'by':'group', 'group':str(wg.group_acronym.acronym), - 'rfcs':'on', 'activedrafts':'on'}) +def construct_group_menu_context(request, group, selected, others): + """Return context with info for the group menu filled in.""" + d = { + "group": group, + "selected": selected, + "can_manage_delegates": can_manage_delegates_in_group(request.user, group), + "can_manage_workflow": can_manage_workflow_in_group(request.user, group), + "can_manage_shepherds": can_manage_shepherds_in_group(request.user, group), + } + + d.update(others) + + return d + +def search_for_group_documents(group): + form = SearchForm({ 'by':'group', 'group': group.acronym or "", 'rfcs':'on', 'activedrafts': 'on' }) docs, meta = retrieve_search_results(form) # get the related docs - form_related = SearchForm({'by':'group', 'name':'-'+str(wg.group_acronym.acronym)+'-', 'activedrafts':'on'}) - docs_related, meta_related = retrieve_search_results(form_related) - docs_related_pruned = [] - for d in docs_related: + form_related = SearchForm({ 'by':'group', 'name': u'-%s-' % group.acronym, 'activedrafts': 'on' }) + raw_docs_related, meta_related = retrieve_search_results(form_related) + + docs_related = [] + for d in raw_docs_related: parts = d.name.split("-", 2); # canonical form draft--wg-etc - if len(parts) >= 3 and parts[1] != "ietf" and parts[2].startswith(wg.group_acronym.acronym + "-"): - docs_related_pruned.append(d) - - docs_related = docs_related_pruned + if len(parts) >= 3 and parts[1] != "ietf" and parts[2].startswith(group.acronym + "-"): + docs_related.append(d) # move call for WG adoption to related cleaned_docs = [] docs_related_names = set(d.name for d in docs_related) for d in docs: - if d.stream_id == "ietf" and d.get_state_slug("draft-stream-ietf") == "c-adopt": + if d.stream_id and d.get_state_slug("draft-stream-%s" % d.stream_id) == "c-adopt": if d.name not in docs_related_names: docs_related.append(d) else: @@ -175,59 +214,75 @@ def wg_documents(request, acronym): docs_related.sort(key=lambda d: d.name) - return wg, concluded, proposed, docs, meta, docs_related, meta_related + return docs, meta, docs_related, meta_related -def wg_documents_txt(request, acronym): - wg, concluded, proposed, docs, meta, docs_related, meta_related = wg_documents(request, acronym) - return HttpResponse(loader.render_to_string('wginfo/wg_documents.txt', {'wg': wg, 'concluded':concluded, 'proposed':proposed, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}),mimetype='text/plain; charset=UTF-8') +def group_documents(request, acronym): + group = get_object_or_404(Group, type="wg", acronym=acronym) -def wg_documents_html(request, acronym): - wg, concluded, proposed, docs, meta, docs_related, meta_related = wg_documents(request, acronym) - return render_to_response('wginfo/wg_documents.html', {'wg': wg, 'concluded':concluded, 'proposed':proposed, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}, RequestContext(request)) + docs, meta, docs_related, meta_related = search_for_group_documents(group) -def wg_charter(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - concluded = wg.status_id in [ 2, 3, ] - proposed = (wg.status_id == 4) + return render_to_response('wginfo/group_documents.html', + construct_group_menu_context(request, group, "documents", { + 'docs': docs, + 'meta': meta, + 'docs_related': docs_related, + 'meta_related': meta_related + }), RequestContext(request)) + +def group_documents_txt(request, acronym): + """Return tabulator-separated rows with documents for group.""" + group = get_object_or_404(Group, type="wg", acronym=acronym) + + docs, meta, docs_related, meta_related = search_for_group_documents(group) + + for d in docs_related: + d.search_heading = u"Related %s" % d.search_heading + + rows = [] + for d in itertools.chain(docs, docs_related): + rfc_number = d.rfc_number() + if rfc_number != None: + name = rfc_number + else: + name = "%s-%s" % (d.name, d.rev) + + rows.append(u"\t".join((d.search_heading.replace("Internet-Draft", ""), name, clean_whitespace(d.title)))) + + return HttpResponse(u"\n".join(rows), mimetype='text/plain; charset=UTF-8') + + +def group_charter(request, acronym): + group = get_object_or_404(Group, type="wg", acronym=acronym) + + fill_in_charter_info(group, include_drafts=False) - fill_in_charter_info(wg) actions = [] - if wg.state_id != "conclude": - actions.append(("Edit WG", urlreverse("wg_edit", kwargs=dict(acronym=wg.acronym)))) + if group.state_id != "conclude": + actions.append((u"Edit %s" % group.type.name, urlreverse("group_edit", kwargs=dict(acronym=group.acronym)))) - e = wg.latest_event(type__in=("changed_state", "requested_close",)) - requested_close = wg.state_id != "conclude" and e and e.type == "requested_close" + e = group.latest_event(type__in=("changed_state", "requested_close",)) + requested_close = group.state_id != "conclude" and e and e.type == "requested_close" - if wg.state_id in ("active", "dormant"): - actions.append(("Request closing WG", urlreverse("wg_conclude", kwargs=dict(acronym=wg.acronym)))) + if group.state_id in ("active", "dormant"): + actions.append((u"Request closing %s" % group.type.name, urlreverse("wg_conclude", kwargs=dict(acronym=group.acronym)))) - context = get_wg_menu_context(wg, "charter") - context.update(dict( - actions=actions, - is_chair=request.user.is_authenticated() and wg.role_set.filter(name="chair", person__user=request.user), - milestones_in_review=wg.groupmilestone_set.filter(state="review"), - requested_close=requested_close, - )) + is_chair = request.user.is_authenticated() and group.role_set.filter(name="chair", person__user=request.user), - return render_to_response('wginfo/wg_charter.html', - context, - RequestContext(request)) + return render_to_response('wginfo/group_charter.html', + construct_group_menu_context(request, group, "charter", { + "actions": actions, + "is_chair": is_chair, + "milestones_in_review": group.groupmilestone_set.filter(state="review"), + "requested_close": requested_close, + }), RequestContext(request)) -def get_wg_menu_context(wg, selected): - # it would probably be better to refactor wginfo into rendering - # the menu separately instead of each view having to include the information - - return dict(wg=wg, concluded=wg.state_id == "conclude", proposed=wg.state_id == "proposed", selected=selected) def history(request, acronym): - wg = get_object_or_404(Group, acronym=acronym) + group = get_object_or_404(Group, acronym=acronym) - events = wg.groupevent_set.all().select_related('by').order_by('-time', '-id') + events = group.groupevent_set.all().select_related('by').order_by('-time', '-id') - context = get_wg_menu_context(wg, "history") - context.update(dict(events=events, - )) - - wg.group_acronym = wg # hack for compatibility with old templates - - return render_to_response('wginfo/history.html', context, RequestContext(request)) + return render_to_response('wginfo/history.html', + construct_group_menu_context(request, group, "history", { + "events": events, + }), RequestContext(request)) From 459bbf39e0a7a6d3a3cf53ccb096061c5a5d2c51 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 4 Sep 2013 08:44:50 +0000 Subject: [PATCH 002/173] Fix spelling mistake - Legacy-Id: 6087 --- ietf/templates/wginfo/group_base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/templates/wginfo/group_base.html b/ietf/templates/wginfo/group_base.html index bd5f47138..ef5000cd0 100644 --- a/ietf/templates/wginfo/group_base.html +++ b/ietf/templates/wginfo/group_base.html @@ -78,7 +78,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% endif %} {% if can_manage_delegates %} - Manage delegations | + Manage delegations | {% endif %} {% if can_manage_shepherds %} From b4feab127a4ebf3a537ed8d7d6deddbba12ee9bc Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 5 Sep 2013 22:08:11 +0000 Subject: [PATCH 003/173] Add basic tests for the information pages in wginfo - Legacy-Id: 6089 --- ietf/wginfo/tests.py | 174 ++++++++++++++++++++++++++++++++++----- ietf/wginfo/testurl.list | 19 ----- 2 files changed, 153 insertions(+), 40 deletions(-) delete mode 100644 ietf/wginfo/testurl.list diff --git a/ietf/wginfo/tests.py b/ietf/wginfo/tests.py index f50d14885..ea5dbb45e 100644 --- a/ietf/wginfo/tests.py +++ b/ietf/wginfo/tests.py @@ -50,36 +50,168 @@ from ietf.name.models import * from ietf.person.models import * from ietf.wginfo.mails import * - -class WgInfoUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) - -class WgFileTestCase(unittest.TestCase): - def testFileExistence(self): - fpath = os.path.join(settings.IETFWG_DESCRIPTIONS_PATH, "tls.desc.txt") - if not os.path.exists(fpath): - print "\nERROR: charter files not found in "+settings.IETFWG_DESCRIPTIONS_PATH - print "They are needed for testing WG charter pages." - print "Download them to a local directory with:" - print "wget -nd -nc -np -r http://www.ietf.org/wg-descriptions/" - print "And set IETFWG_DESCRIPTIONS_PATH in settings_local.py\n" - -class WgOverviewTestCase(django.test.TestCase): +class GroupPagesTests(django.test.TestCase): fixtures = ["names"] - def test_overview(self): - make_test_data() + def setUp(self): + self.charter_dir = os.path.abspath("tmp-charter-dir") + os.mkdir(self.charter_dir) + settings.CHARTER_PATH = self.charter_dir - wg = Group.objects.get(acronym="mars") - wg.charter.set_state(State.objects.get(used=True, type="charter", slug="intrev")) + def tearDown(self): + shutil.rmtree(self.charter_dir) + + def test_active_wgs(self): + draft = make_test_data() + group = draft.group + + url = urlreverse('ietf.wginfo.views.active_wgs') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.parent.name in r.content) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.ad.plain_name() in r.content) + + def test_wg_summaries(self): + draft = make_test_data() + group = draft.group + + chair = Email.objects.filter(role__group=group, role__name="chair")[0] + + with open(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev)), "w") as f: + f.write("This is a charter.") + + url = urlreverse('ietf.wginfo.views.wg_summary_area') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.parent.name in r.content) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(chair.address in r.content) + + url = urlreverse('ietf.wginfo.views.wg_summary_acronym') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(chair.address in r.content) + + url = urlreverse('ietf.wginfo.views.wg_charters') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.ad.plain_name() in r.content) + self.assertTrue(chair.address in r.content) + self.assertTrue("This is a charter." in r.content) + + url = urlreverse('ietf.wginfo.views.wg_charters_by_acronym') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.ad.plain_name() in r.content) + self.assertTrue(chair.address in r.content) + self.assertTrue("This is a charter." in r.content) + + def test_chartering_wgs(self): + draft = make_test_data() + group = draft.group + group.charter.set_state(State.objects.get(used=True, type="charter", slug="intrev")) url = urlreverse('ietf.wginfo.views.chartering_wgs') r = self.client.get(url) self.assertEquals(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("mars")')), 1) + self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1) + def test_bofs(self): + draft = make_test_data() + group = draft.group + group.state_id = "bof" + group.save() + + url = urlreverse('ietf.wginfo.views.bofs') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1) + + def test_group_documents(self): + draft = make_test_data() + group = draft.group + + draft2 = Document.objects.create( + name="draft-somebody-mars-test", + time=datetime.datetime.now(), + type_id="draft", + title="Test By Somebody", + stream_id="ietf", + group=Group.objects.get(type="individ"), + abstract="Abstract.", + rev="01", + pages=2, + intended_std_level_id="ps", + shepherd=None, + ad=None, + expires=datetime.datetime.now() + datetime.timedelta(days=10), + notify="", + note="", + ) + + draft2.set_state(State.objects.get(used=True, type="draft", slug="active")) + DocAlias.objects.create( + document=draft2, + name=draft2.name, + ) + + url = urlreverse('ietf.wginfo.views.group_documents', kwargs=dict(acronym=group.acronym)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(draft.name in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.acronym in r.content) + + self.assertTrue(draft2.name in r.content) + + def test_group_charter(self): + draft = make_test_data() + group = draft.group + + with open(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev)), "w") as f: + f.write("This is a charter.") + + milestone = GroupMilestone.objects.create( + group=group, + state_id="active", + desc="Get Work Done", + due=datetime.date.today() + datetime.timedelta(days=100)) + milestone.docs.add(draft) + + url = urlreverse('ietf.wginfo.views.group_charter', kwargs=dict(acronym=group.acronym)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.name in r.content) + self.assertTrue(group.acronym in r.content) + self.assertTrue("This is a charter." in r.content) + self.assertTrue(milestone.desc in r.content) + self.assertTrue(milestone.docs.all()[0].name in r.content) + + def test_history(self): + draft = make_test_data() + group = draft.group + + e = GroupEvent.objects.create( + group=group, + desc="Something happened.", + type="added_comment", + by=Person.objects.get(name="(System)")) + + url = urlreverse('ietf.wginfo.views.history', kwargs=dict(acronym=group.acronym)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(e.desc in r.content) class WgEditTestCase(django.test.TestCase): fixtures = ["names"] diff --git a/ietf/wginfo/testurl.list b/ietf/wginfo/testurl.list deleted file mode 100644 index 2cf38b30a..000000000 --- a/ietf/wginfo/testurl.list +++ /dev/null @@ -1,19 +0,0 @@ -200 /wg/ -404 /wg/nosuchgroup/ -200 /wg/tls/ -200 /wg/tls/charter/ -200 /wg/mobike/ # concluded -200 /wg/mobike/charter/ -200 /wg/catnip/ # concluded very long time ago -200 /wg/catnip/charter/ # concluded very long time ago -404 /wg/saag/ # not a WG -404 /wg/saag/charter/ # not a WG - -200 /wg/1wg-summary.txt -200 /wg/1wg-summary-by-acronym.txt -301 /wg/summary.txt -301 /wg/summary-by-area.txt -301 /wg/summary-by-acronym.txt -200,heavy /wg/1wg-charters.txt -200,heavy /wg/1wg-charters-by-acronym.txt - From 3f9327eefe72b76fc3fd6cb53f550785a8f7cb18 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 6 Sep 2013 22:38:57 +0000 Subject: [PATCH 004/173] Fix crash bug in /wg//charter/ page for BOFs - Legacy-Id: 6092 --- ietf/wginfo/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index dd2070200..362beab76 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -63,7 +63,10 @@ def fill_in_charter_info(group, include_drafts=False): milestone_state = "charter" if group.state_id == "proposed" else "active" group.milestones = group.groupmilestone_set.filter(state=milestone_state).order_by('due') - group.charter_text = get_charter_text(group) + if group.charter: + group.charter_text = get_charter_text(group) + else: + group.charter_text = u"Not chartered yet." if include_drafts: aliases = DocAlias.objects.filter(document__type="draft", document__group=group).select_related('document').order_by("name") From 5a3cccce7c1b40a346339bb9ae59125365311312 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 9 Sep 2013 16:46:34 +0000 Subject: [PATCH 005/173] Fix subtle is_chair bug and construct group_documents_txt state names directly from state instead of using search headings - Legacy-Id: 6113 --- ietf/wginfo/views.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 362beab76..446c1ba59 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -238,8 +238,11 @@ def group_documents_txt(request, acronym): docs, meta, docs_related, meta_related = search_for_group_documents(group) + for d in docs: + d.prefix = d.get_state().name + for d in docs_related: - d.search_heading = u"Related %s" % d.search_heading + d.prefix = u"Related %s" % d.get_state().name rows = [] for d in itertools.chain(docs, docs_related): @@ -249,7 +252,7 @@ def group_documents_txt(request, acronym): else: name = "%s-%s" % (d.name, d.rev) - rows.append(u"\t".join((d.search_heading.replace("Internet-Draft", ""), name, clean_whitespace(d.title)))) + rows.append(u"\t".join((d.prefix, name, clean_whitespace(d.title)))) return HttpResponse(u"\n".join(rows), mimetype='text/plain; charset=UTF-8') @@ -269,7 +272,7 @@ def group_charter(request, acronym): if group.state_id in ("active", "dormant"): actions.append((u"Request closing %s" % group.type.name, urlreverse("wg_conclude", kwargs=dict(acronym=group.acronym)))) - is_chair = request.user.is_authenticated() and group.role_set.filter(name="chair", person__user=request.user), + is_chair = request.user.is_authenticated() and group.role_set.filter(name="chair", person__user=request.user) return render_to_response('wginfo/group_charter.html', construct_group_menu_context(request, group, "charter", { From 026ed3f8df422cdfd241168774d0087e4814b3ef Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 10 Sep 2013 16:13:59 +0000 Subject: [PATCH 006/173] Add statehelp template filter for outputting a little help icon for a state with a tooltip and link to the description - Legacy-Id: 6133 --- ietf/doc/templatetags/ietf_filters.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 21f9e4b80..2e7dc8cf6 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -9,6 +9,7 @@ from django.template.defaultfilters import linebreaksbr, wordwrap, stringfilter, from django.template import resolve_variable from django.utils.safestring import mark_safe, SafeData from django.utils import simplejson +from django.utils.html import strip_tags try: from email import utils as emailutils except ImportError: @@ -475,6 +476,14 @@ def state(doc, slug): slug = "%s-stream-%s" % (doc.type_id, doc.stream_id) return doc.get_state(slug) +@register.filter +def statehelp(state): + "Output help icon with tooltip for state." + from django.core.urlresolvers import reverse as urlreverse + tooltip = escape(strip_tags(state.desc)) + url = urlreverse("state_help", kwargs=dict(type=state.type_id)) + "#" + state.slug + return mark_safe('?' % (url, tooltip)) + def _test(): import doctest doctest.testmod() @@ -483,11 +492,10 @@ if __name__ == "__main__": _test() @register.filter -def plural(text, list, arg=u's'): +def plural(text, seq, arg=u's'): "Similar to pluralize, but looks at the text, too" from django.template.defaultfilters import pluralize if text.endswith('s'): return text else: - return text + pluralize(len(list), arg) - \ No newline at end of file + return text + pluralize(len(seq), arg) From 95fdfb5d8a9e59d3bf930d6adf5b556eb46e9168 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 10 Sep 2013 16:15:18 +0000 Subject: [PATCH 007/173] Add tags for streams to state help - Legacy-Id: 6134 --- ietf/doc/views_help.py | 8 ++++++-- ietf/templates/doc/state_help.html | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ietf/doc/views_help.py b/ietf/doc/views_help.py index 68104d624..936c27bf1 100644 --- a/ietf/doc/views_help.py +++ b/ietf/doc/views_help.py @@ -3,12 +3,14 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from ietf.doc.models import * +from ietf.doc.utils import get_tags_for_stream_id def state_help(request, type): slug, title = { "draft-iesg": ("draft-iesg", "IESG States For Internet-Drafts"), "draft-rfceditor": ("draft-rfceditor", "RFC Editor States For Internet-Drafts"), "draft-iana-action": ("draft-iana-action", "IANA Action States For Internet-Drafts"), + "draft-stream-ietf": ("draft-stream-ietf", "IETF Stream States For Internet-Drafts"), "charter": ("charter", "Charter States"), "conflict-review": ("conflrev", "Conflict Review States"), "status-change": ("statchg", "RFC Status Change States"), @@ -35,6 +37,9 @@ def state_help(request, type): states.insert(0, fake_state) tags = DocTagName.objects.filter(slug__in=IESG_SUBSTATE_TAGS) + elif state_type.slug.startswith("draft-stream-"): + possible = get_tags_for_stream_id(state_type.slug.replace("draft-stream-", "")) + tags = DocTagName.objects.filter(slug__in=possible) return render_to_response("doc/state_help.html", { "title": title, @@ -42,5 +47,4 @@ def state_help(request, type): "states": states, "has_next_states": has_next_states, "tags": tags, - }, - context_instance=RequestContext(request)) + }, context_instance=RequestContext(request)) diff --git a/ietf/templates/doc/state_help.html b/ietf/templates/doc/state_help.html index e0c1e3131..a328d6c87 100644 --- a/ietf/templates/doc/state_help.html +++ b/ietf/templates/doc/state_help.html @@ -24,7 +24,7 @@ {% for state in states %} {{ state.name }} - {{ state.desc|linebreaksbr }} + {{ state.desc|safe|linebreaksbr }} {% if has_next_states %} {% for s in state.next_states.all %} From 3c8f5d352197edb23a44fca2b0ade5058955e968 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 10 Sep 2013 16:16:11 +0000 Subject: [PATCH 008/173] CSS styling for state help icon - Legacy-Id: 6135 --- static/css/base2.css | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/static/css/base2.css b/static/css/base2.css index f51504aae..20149dbfc 100644 --- a/static/css/base2.css +++ b/static/css/base2.css @@ -363,6 +363,24 @@ span.fieldRequired { color: red; } +.state-help-icon { + display: inline-block; + margin-left: 0.2em; + padding: 0 0.2em; + font-weight: bold; + font-style: normal; + font-size: 90%; + color: #999; + background-color: #ddd; + text-decoration: none; +} + +.state-help-icon:hover { + color: #eee; + background-color: #bbb; + transition-duration: 0.2s; +} + /* js styles */ .js-info { background-color: #FFDD88; From 947345ecf239d19c3d113fce4d4a276ba90dcbd2 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 10 Sep 2013 16:18:14 +0000 Subject: [PATCH 009/173] Remove link to now obsolete shepherd pages, move manage workflow to wginfo, fix a couple of oddities on the workflow page - Legacy-Id: 6136 --- .../customize_workflow.html} | 40 +++--- ietf/templates/wginfo/group_base.html | 14 +- ietf/templates/wginfo/group_charter.html | 2 +- ietf/wgchairs/urls.py | 2 - ietf/wgchairs/views.py | 127 ------------------ ietf/wginfo/edit.py | 80 ++++++++++- ietf/wginfo/urls.py | 2 +- ietf/wginfo/views.py | 11 +- 8 files changed, 105 insertions(+), 173 deletions(-) rename ietf/templates/{wgchairs/manage_workflowREDESIGN.html => wginfo/customize_workflow.html} (80%) diff --git a/ietf/templates/wgchairs/manage_workflowREDESIGN.html b/ietf/templates/wginfo/customize_workflow.html similarity index 80% rename from ietf/templates/wgchairs/manage_workflowREDESIGN.html rename to ietf/templates/wginfo/customize_workflow.html index 4a385afaf..d1c939e64 100644 --- a/ietf/templates/wgchairs/manage_workflowREDESIGN.html +++ b/ietf/templates/wginfo/customize_workflow.html @@ -1,13 +1,6 @@ -{% extends "wginfo/wg_base.html" %} +{% extends "base.html" %} -{% block wg_titledetail %}Manage Workflow{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock pagehead %} +{% block title %}Customize Workflow for {{ group.acronym }} {{ group.type.name }}{% endblock %} {% block morecss %} {{ block.super }} @@ -53,13 +46,20 @@ } {% endblock %} -{% block wg_content %} -
-

Edit workflow

+{% block content %} +{% load ietf_filters %} -

Below you can customize the draft states and tags used in the {{ wg.acronym }} WG. Note that some states are mandatory for WG operation and cannot be deactivated.

+
+ +

Customize Workflow for {{ group.acronym }} {{ group.type.name }}

+ +

Below you can customize the draft states and tags used in the +{{ group.acronym }} {{ group.type.name }}. Note that some states are +mandatory for group operation and cannot be deactivated.

+ +

You can see the default Working Group I-D State Diagram +in Section 4.1 of RFC6174.

-

You can see the default Working Group I-D State Diagram in Section 4.1 of RFC6174.

States

@@ -90,11 +90,7 @@ {% endif %} -
- {{ state.name }} {% if not state.used %} (not used in {{ wg.acronym }}){% endif %} - + -
-
{{ state.desc|safe|linebreaks }}
+ {{ state.name }} {% if not state.used %} (not used in {{ group.acronym }}){% endif %} {{ state|statehelp }}
@@ -142,11 +138,14 @@ - {{ tag.name }} {% if not tag.used %} (not used in {{ wg.acronym }}){% endif %} + {{ tag.name }} {% if not tag.used %} (not used in {{ group.acronym }}){% endif %} {% endfor %} +{% endblock content %} + +{% block js %} - {% endblock %} diff --git a/ietf/templates/wginfo/group_base.html b/ietf/templates/wginfo/group_base.html index ef5000cd0..1496a7e57 100644 --- a/ietf/templates/wginfo/group_base.html +++ b/ietf/templates/wginfo/group_base.html @@ -32,7 +32,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% endcomment %} -{% load ietf_filters wgchairs_tags %} +{% load ietf_filters %} {% block title %}{{ group.name }} ({{ group.acronym }}) - {% block group_subtitle %}{% endblock %}{% endblock %} {% block morecss %} @@ -73,18 +73,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Documents | Charter | - {% if can_manage_workflow %} - Manage workflow | - {% endif %} - - {% if can_manage_delegates %} - Manage delegations | - {% endif %} - - {% if can_manage_shepherds %} - Manage shepherds | - {% endif %} - History | {% if group.list_archive|startswith:"http:" or group.list_archive|startswith:"https:" or group.list_archive|startswith:"ftp:" %} List Archive » | diff --git a/ietf/templates/wginfo/group_charter.html b/ietf/templates/wginfo/group_charter.html index fb4d448b1..7175748a3 100644 --- a/ietf/templates/wginfo/group_charter.html +++ b/ietf/templates/wginfo/group_charter.html @@ -130,7 +130,7 @@ is occasionally incorrect. {% if user|has_role:"Area Director,Secretariat" %}
{% for name, url in actions %} - {{ name }} + {{ name }} {% if not forloop.last %} | {% endif %} {% endfor %}
{% endif %} diff --git a/ietf/wgchairs/urls.py b/ietf/wgchairs/urls.py index d8cadb8ee..b9b32c240 100644 --- a/ietf/wgchairs/urls.py +++ b/ietf/wgchairs/urls.py @@ -3,7 +3,5 @@ from django.conf.urls.defaults import patterns, url urlpatterns = patterns('ietf.wgchairs.views', - url(r'^workflows/$', 'manage_workflow', name='manage_workflow'), url(r'^delegates/$', 'manage_delegates', name='manage_delegates'), - url(r'^shepherds/$', 'wg_shepherd_documents', name='manage_shepherds'), ) diff --git a/ietf/wgchairs/views.py b/ietf/wgchairs/views.py index 3bcd6e026..1f9552e3d 100644 --- a/ietf/wgchairs/views.py +++ b/ietf/wgchairs/views.py @@ -51,133 +51,6 @@ def manage_delegates(request, acronym): }, RequestContext(request)) -def manage_workflow(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_workflow_in_group(user, wg): - return HttpResponseForbidden("You don't have permission to access this view") - workflow = get_workflow_for_wg(wg) - default_workflow = get_default_workflow_for_wg() - formset = None - if request.method == 'POST': - form = workflow_form_factory(request, wg=wg, user=user) - if form.is_valid(): - form.save() - elif isinstance(form, TransitionFormSet): - formset = form - tags = workflow.selected_tags.all() - default_tags = default_workflow.annotation_tags.all() - states = workflow.selected_states.all().order_by('statedescription__order') - default_states = default_workflow.states.all().order_by('statedescription__order') - for i in default_states: - if states.filter(name=i.name).count() == 1: - i.used = True - if i.name in REQUIRED_STATES: - i.freeze = True - for i in default_tags: - if tags.filter(name=i.name).count() == 1: - i.used = True - if not formset: - formset = TransitionFormSet(queryset=workflow.transitions.all(), user=user, wg=wg) - - return render_to_response('wgchairs/manage_workflow.html', - {'wg': wg, - 'workflow': workflow, - 'default_workflow': default_workflow, - 'states': states, - 'tags': tags, - 'default_states': default_states, - 'default_tags': default_tags, - 'formset': formset, - 'selected': 'manage_workflow', - }, RequestContext(request)) - -def manage_workflowREDESIGN(request, acronym): - from ietf.doc.models import State - from ietf.group.models import GroupStateTransitions - - MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub') - - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_workflow_in_group(user, wg): - return HttpResponseForbidden("You don't have permission to access this view") - - if request.method == 'POST': - action = request.POST.get("action") - if action == "setstateactive": - active = request.POST.get("active") == "1" - try: - state = State.objects.exclude(slug__in=MANDATORY_STATES).get(pk=request.POST.get("state")) - except State.DoesNotExist: - return HttpResponse("Invalid state %s" % request.POST.get("state")) - - if active: - wg.unused_states.remove(state) - else: - wg.unused_states.add(state) - - if action == "setnextstates": - try: - state = State.objects.get(pk=request.POST.get("state")) - except State.DoesNotExist: - return HttpResponse("Invalid state %s" % request.POST.get("state")) - - next_states = State.objects.filter(used=True, type='draft-stream-ietf', pk__in=request.POST.getlist("next_states")) - unused = wg.unused_states.all() - if set(next_states.exclude(pk__in=unused)) == set(state.next_states.exclude(pk__in=unused)): - # just use the default - wg.groupstatetransitions_set.filter(state=state).delete() - else: - transitions, _ = GroupStateTransitions.objects.get_or_create(group=wg, state=state) - transitions.next_states = next_states - - if action == "settagactive": - active = request.POST.get("active") == "1" - try: - tag = DocTagName.objects.get(pk=request.POST.get("tag")) - except DocTagName.DoesNotExist: - return HttpResponse("Invalid tag %s" % request.POST.get("tag")) - - if active: - wg.unused_tags.remove(tag) - else: - wg.unused_tags.add(tag) - - - # put some info for the template on tags and states - unused_tags = wg.unused_tags.all().values_list('slug', flat=True) - tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id("ietf")) - for t in tags: - t.used = t.slug not in unused_tags - - unused_states = wg.unused_states.all().values_list('slug', flat=True) - states = State.objects.filter(used=True, type="draft-stream-ietf") - transitions = dict((o.state, o) for o in wg.groupstatetransitions_set.all()) - for s in states: - s.used = s.slug not in unused_states - s.mandatory = s.slug in MANDATORY_STATES - - default_n = s.next_states.all() - if s in transitions: - n = transitions[s].next_states.all() - else: - n = default_n - - s.next_states_checkboxes = [(x in n, x in default_n, x) for x in states] - s.used_next_states = [x for x in n if x.slug not in unused_states] - - return render_to_response('wgchairs/manage_workflowREDESIGN.html', - {'wg': wg, - 'states': states, - 'tags': tags, - 'selected': 'manage_workflow', - }, RequestContext(request)) - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - manage_workflow = manage_workflowREDESIGN - def wg_shepherd_documents(request, acronym): wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) user = request.user diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index 670d876c5..eb73783c0 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -20,7 +20,7 @@ from ietf.group.models import * from ietf.group.utils import save_group_in_history from ietf.wgcharter.mails import email_secretariat from ietf.person.forms import EmailsField - +from ietf.doc.utils import get_tags_for_stream_id class WGForm(forms.Form): name = forms.CharField(max_length=255, label="WG Name", required=True) @@ -320,3 +320,81 @@ def conclude(request, acronym): dict(form=form, wg=wg), context_instance=RequestContext(request)) + + +def customize_workflow(request, acronym): + MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub') + + group = get_object_or_404(Group, acronym=acronym, type="wg") + if not request.user.is_authenticated() or not (has_role(request.user, "Secretariat") or group.role_set.filter(name="chair", person__user=request.user)): + return HttpResponseForbidden("You don't have permission to access this view") + + if request.method == 'POST': + action = request.POST.get("action") + if action == "setstateactive": + active = request.POST.get("active") == "1" + try: + state = State.objects.exclude(slug__in=MANDATORY_STATES).get(pk=request.POST.get("state")) + except State.DoesNotExist: + return HttpResponse("Invalid state %s" % request.POST.get("state")) + + if active: + group.unused_states.remove(state) + else: + group.unused_states.add(state) + + if action == "setnextstates": + try: + state = State.objects.get(pk=request.POST.get("state")) + except State.DoesNotExist: + return HttpResponse("Invalid state %s" % request.POST.get("state")) + + next_states = State.objects.filter(used=True, type='draft-stream-ietf', pk__in=request.POST.getlist("next_states")) + unused = group.unused_states.all() + if set(next_states.exclude(pk__in=unused)) == set(state.next_states.exclude(pk__in=unused)): + # just use the default + group.groupstatetransitions_set.filter(state=state).delete() + else: + transitions, _ = GroupStateTransitions.objects.get_or_create(group=group, state=state) + transitions.next_states = next_states + + if action == "settagactive": + active = request.POST.get("active") == "1" + try: + tag = DocTagName.objects.get(pk=request.POST.get("tag")) + except DocTagName.DoesNotExist: + return HttpResponse("Invalid tag %s" % request.POST.get("tag")) + + if active: + group.unused_tags.remove(tag) + else: + group.unused_tags.add(tag) + + + # put some info for the template on tags and states + unused_tags = group.unused_tags.all().values_list('slug', flat=True) + tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id("ietf")) + for t in tags: + t.used = t.slug not in unused_tags + + unused_states = group.unused_states.all().values_list('slug', flat=True) + states = State.objects.filter(used=True, type="draft-stream-ietf") + transitions = dict((o.state, o) for o in group.groupstatetransitions_set.all()) + for s in states: + s.used = s.slug not in unused_states + s.mandatory = s.slug in MANDATORY_STATES + + default_n = s.next_states.all() + if s in transitions: + n = transitions[s].next_states.all() + else: + n = default_n + + s.next_states_checkboxes = [(x in n, x in default_n, x) for x in states] + s.used_next_states = [x for x in n if x.slug not in unused_states] + + return render_to_response('wginfo/customize_workflow.html', { + 'group': group, + 'states': states, + 'tags': tags, + }, RequestContext(request)) diff --git a/ietf/wginfo/urls.py b/ietf/wginfo/urls.py index 956ee2de6..bad932b21 100644 --- a/ietf/wginfo/urls.py +++ b/ietf/wginfo/urls.py @@ -29,6 +29,6 @@ urlpatterns = patterns('', (r'^(?P[a-zA-Z0-9-]+)/milestones/charter/$', milestones.edit_milestones, {'milestone_set': "charter"}, "wg_edit_charter_milestones"), (r'^(?P[a-zA-Z0-9-]+)/milestones/charter/reset/$', milestones.reset_charter_milestones, None, "wg_reset_charter_milestones"), (r'^(?P[a-zA-Z0-9-]+)/ajax/searchdocs/$', milestones.ajax_search_docs, None, "wg_ajax_search_docs"), + (r'^(?P[a-zA-Z0-9-]+)/workflow/$', edit.customize_workflow), (r'^(?P[^/]+)/management/', include('ietf.wgchairs.urls')), - ) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 446c1ba59..b45310929 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -49,10 +49,6 @@ from ietf.person.models import Email from ietf.group.utils import get_charter_text from ietf.doc.templatetags.ietf_filters import clean_whitespace -from ietf.wgchairs.accounts import (can_manage_workflow_in_group, - can_manage_delegates_in_group, - can_manage_shepherds_in_group) - def fill_in_charter_info(group, include_drafts=False): group.areadirector = group.ad.role_email("ad", group.parent) if group.ad else None @@ -179,9 +175,6 @@ def construct_group_menu_context(request, group, selected, others): d = { "group": group, "selected": selected, - "can_manage_delegates": can_manage_delegates_in_group(request.user, group), - "can_manage_workflow": can_manage_workflow_in_group(request.user, group), - "can_manage_shepherds": can_manage_shepherds_in_group(request.user, group), } d.update(others) @@ -274,6 +267,10 @@ def group_charter(request, acronym): is_chair = request.user.is_authenticated() and group.role_set.filter(name="chair", person__user=request.user) + if is_chair or has_role(request.user, "Secretariat"): + actions.append((u"Manage delegates", urlreverse("manage_delegates", kwargs=dict(acronym=group.acronym)))) + actions.append((u"Customize workflow", urlreverse("ietf.wginfo.edit.customize_workflow", kwargs=dict(acronym=group.acronym)))) + return render_to_response('wginfo/group_charter.html', construct_group_menu_context(request, group, "charter", { "actions": actions, From 9409814ada5892b7043968265aff6613790f1f51 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 10 Sep 2013 16:22:11 +0000 Subject: [PATCH 010/173] Fix missing import - Legacy-Id: 6137 --- ietf/wginfo/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index b45310929..87f4d8caf 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -48,6 +48,7 @@ from ietf.doc.utils import get_chartering_type from ietf.person.models import Email from ietf.group.utils import get_charter_text from ietf.doc.templatetags.ietf_filters import clean_whitespace +from ietf.ietfauth.utils import has_role def fill_in_charter_info(group, include_drafts=False): From 8657e4fd08a4a7a510e3c17f2cd089fedcf2b917 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 10 Sep 2013 16:23:22 +0000 Subject: [PATCH 011/173] Redirect when POSTing on workflow customization page to prevent the history from filling up with spam entries - Legacy-Id: 6138 --- ietf/wginfo/edit.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index eb73783c0..09814d3a0 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -343,6 +343,10 @@ def customize_workflow(request, acronym): else: group.unused_states.add(state) + # redirect so the back button works correctly, otherwise + # repeated POSTs fills up the history + return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym) + if action == "setnextstates": try: state = State.objects.get(pk=request.POST.get("state")) @@ -358,6 +362,8 @@ def customize_workflow(request, acronym): transitions, _ = GroupStateTransitions.objects.get_or_create(group=group, state=state) transitions.next_states = next_states + return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym) + if action == "settagactive": active = request.POST.get("active") == "1" try: @@ -370,6 +376,8 @@ def customize_workflow(request, acronym): else: group.unused_tags.add(tag) + return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym) + # put some info for the template on tags and states unused_tags = group.unused_tags.all().values_list('slug', flat=True) From 36d4710393b0967dcd44c0bc05baa79a4390eddb Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 11 Sep 2013 17:28:33 +0000 Subject: [PATCH 012/173] Fix styling of next states in workflow customization, replace "+" with "+ customize" - Legacy-Id: 6142 --- ietf/templates/wginfo/customize_workflow.html | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/ietf/templates/wginfo/customize_workflow.html b/ietf/templates/wginfo/customize_workflow.html index d1c939e64..082ecfe3d 100644 --- a/ietf/templates/wginfo/customize_workflow.html +++ b/ietf/templates/wginfo/customize_workflow.html @@ -5,45 +5,25 @@ {% block morecss %} {{ block.super }} .state-table .inactive, -.tag-table .inactive { - font-style: italic; +.tag-table .inactive { font-style: italic; color: #666; } +.state-table .state { margin-bottom: 0.1em; } +.state-table .set-next-states label { display: block; cursor: pointer; } +.state-table .set-next-states label input { vertical-align: middle; } +.state-table .set-state input, .set-tag input { width: 6em; } +.state-table .toggled { display: none; } +.state-table .toggler { color: #666; -} -.state-table .state { - padding-right: 0.6em; -} -.set-next-states { - margin-top: 1em; -} -.set-next-states label { - display: block; - cursor: pointer; -} -.set-next-states label input { - vertical-align: middle; -} -.set-state input, .set-tag input { - width: 6em; -} -.toggled { - display: none; -} -.toggler { - color: #000; + background: #ddd; + font-weight: bold; text-decoration: none; - padding: 0px 3px; + padding: 1px 3px; display: inline-block; margin-left: 0.5em; - font-size: 15px; - font-weight: bold; -} -.inactive .toggler { - color: #666; -} -.toggler:hover { - background-color: #999; - color: #fff; + margin-top: 0.5em; + margin-bottom: 0.5em; } +.state-table .inactive .toggler { color: #aaa; background: #eee; } +.state-table .toggler:hover { background-color: #999; color: #fff; } {% endblock %} {% block content %} @@ -95,11 +75,11 @@ in Section 4.1 of RFC61
Select the next states: @@ -153,11 +133,11 @@ jQuery('.state-table .toggler').click(function(e) { var toggler = jQuery(this), toggled = toggler.parent().siblings(".toggled"); if (toggled.is(":hidden")) { - toggler.html("–"); + toggler.html("– customize"); toggled.slideDown(300); } else { - toggler.html("+"); + toggler.html("+ customize"); toggled.slideUp(300); } }); From 5493f3c61346fbb89c6ba8ef15acc13a49e8a7d3 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 12 Sep 2013 16:36:10 +0000 Subject: [PATCH 013/173] Fix missing import statement in for customize_workflow - Legacy-Id: 6146 --- ietf/wginfo/edit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index 09814d3a0..068a62b10 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -1,8 +1,9 @@ # edit/create view for WGs -import re, os, string, datetime, shutil +import re, os, datetime, shutil from django.shortcuts import render_to_response, get_object_or_404, redirect +from django.http import HttpResponseForbidden from django.core.urlresolvers import reverse from django.template import RequestContext from django import forms From 570b7577477979c2fdf3e1bfa9a6d8857fa72f03 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 12 Sep 2013 16:36:27 +0000 Subject: [PATCH 014/173] Move group workflow tests to wginfo - Legacy-Id: 6147 --- ietf/wginfo/tests.py | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/ietf/wginfo/tests.py b/ietf/wginfo/tests.py index ea5dbb45e..7615e23bc 100644 --- a/ietf/wginfo/tests.py +++ b/ietf/wginfo/tests.py @@ -817,3 +817,75 @@ class MilestoneTestCase(django.test.TestCase): self.assertTrue(group.acronym in outbox[-1]["Subject"]) self.assertTrue(m1.desc in unicode(outbox[-1])) self.assertTrue(m2.desc in unicode(outbox[-1])) + +class CustomizeWorkflowTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_customize_workflow(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + + url = urlreverse('ietf.wginfo.edit.customize_workflow', kwargs=dict(acronym=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-lc") + self.assertTrue(state not in group.unused_states.all()) + + # get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='0']")), 1) + + # deactivate state + r = self.client.post(url, + dict(action="setstateactive", + state=state.pk, + active="0")) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='1']")), 1) + group = Group.objects.get(acronym=group.acronym) + self.assertTrue(state in group.unused_states.all()) + + # change next states + state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-doc") + next_states = State.objects.filter(used=True, type=b"draft-stream-ietf", slug__in=["parked", "dead", "wait-wgw", 'sub-pub']).values_list('pk', flat=True) + r = self.client.post(url, + dict(action="setnextstates", + state=state.pk, + next_states=next_states)) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + self.assertEquals(len(q("form.set-next-states").find("input[name=state][value='%s']" % state.pk).parents('form').find("input[name=next_states][checked=checked]")), len(next_states)) + transitions = GroupStateTransitions.objects.filter(group=group, state=state) + self.assertEquals(len(transitions), 1) + self.assertEquals(set(transitions[0].next_states.values_list("pk", flat=True)), set(next_states)) + + # change them back to default + next_states = state.next_states.values_list("pk", flat=True) + r = self.client.post(url, + dict(action="setnextstates", + state=state.pk, + next_states=next_states)) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + transitions = GroupStateTransitions.objects.filter(group=group, state=state) + self.assertEquals(len(transitions), 0) + + # deactivate tag + tag = DocTagName.objects.get(slug="w-expert") + r = self.client.post(url, + dict(action="settagactive", + tag=tag.pk, + active="0")) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + self.assertEquals(len(q('form').find('input[name=tag][value="%s"]' % tag.pk).parents("form").find("input[name=active]")), 1) + group = Group.objects.get(acronym=group.acronym) + self.assertTrue(tag in group.unused_tags.all()) From f4761ba7c13649eef5ae28e15047466eed330ce8 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 19 Sep 2013 11:01:32 +0000 Subject: [PATCH 015/173] Get rid of unused wgchairs references - Legacy-Id: 6193 --- ietf/ietfworkflows/templatetags/ietf_streams.py | 2 -- ietf/ietfworkflows/templatetags/ietf_streams_redesign.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/ietf/ietfworkflows/templatetags/ietf_streams.py b/ietf/ietfworkflows/templatetags/ietf_streams.py index f9e69803f..5d8b5ca51 100644 --- a/ietf/ietfworkflows/templatetags/ietf_streams.py +++ b/ietf/ietfworkflows/templatetags/ietf_streams.py @@ -5,8 +5,6 @@ from django.core.urlresolvers import reverse as urlreverse from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper from ietf.ietfworkflows.utils import (get_workflow_for_draft, get_state_for_draft) -from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document, - can_manage_writeup_of_a_document) from ietf.ietfworkflows.streams import get_stream_from_wrapper from ietf.ietfworkflows.models import Stream from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, diff --git a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py b/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py index 978e99e74..852911126 100644 --- a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py +++ b/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py @@ -5,8 +5,6 @@ from django.core.urlresolvers import reverse as urlreverse from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper from ietf.ietfworkflows.utils import (get_workflow_for_draft, get_state_for_draft) -from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document, - can_manage_writeup_of_a_document) from ietf.ietfworkflows.streams import get_stream_from_wrapper from ietf.ietfworkflows.models import Stream from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, From 3562c5ef0664055fc8f4a9d65ebff79288b938b3 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 19 Sep 2013 11:02:24 +0000 Subject: [PATCH 016/173] Rewrite slightly to avoid using wgchairs.accounts.get_person_for_user - Legacy-Id: 6194 --- ietf/ietfworkflows/forms.py | 6 ++++-- ietf/secr/announcement/forms.py | 3 +-- ietf/secr/announcement/views.py | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ietf/ietfworkflows/forms.py b/ietf/ietfworkflows/forms.py index 7f9dd1fdf..c5cef4f57 100644 --- a/ietf/ietfworkflows/forms.py +++ b/ietf/ietfworkflows/forms.py @@ -7,7 +7,6 @@ from workflows.models import State from workflows.utils import set_workflow_for_object from ietf.idtracker.models import PersonOrOrgInfo, IETFWG, InternetDraft -from ietf.wgchairs.accounts import get_person_for_user from ietf.ietfworkflows.models import Stream, StreamDelegate from ietf.ietfworkflows.utils import (get_workflow_for_draft, get_workflow_for_wg, get_state_for_draft, get_state_by_name, @@ -33,7 +32,10 @@ class StreamDraftForm(forms.Form): def __init__(self, *args, **kwargs): self.draft = kwargs.pop('draft', None) self.user = kwargs.pop('user', None) - self.person = get_person_for_user(self.user) + try: + self.person = self.user.get_profile() + except: + self.person = None self.workflow = get_workflow_for_draft(self.draft) self.message = {} super(StreamDraftForm, self).__init__(*args, **kwargs) diff --git a/ietf/secr/announcement/forms.py b/ietf/secr/announcement/forms.py index 64293c324..e74498d57 100644 --- a/ietf/secr/announcement/forms.py +++ b/ietf/secr/announcement/forms.py @@ -7,7 +7,6 @@ from ietf.secr.utils.group import current_nomcom from ietf.message.models import Message from ietf.ietfauth.decorators import has_role -from ietf.wgchairs.accounts import get_person_for_user # --------------------------------------------- # Globals @@ -158,7 +157,7 @@ class AnnounceForm(forms.ModelForm): def save(self, *args, **kwargs): user = kwargs.pop('user') message = super(AnnounceForm, self).save(commit=False) - message.by = get_person_for_user(user) + message.by = user.get_profile() if self.cleaned_data['to'] == 'Other...': message.to = self.cleaned_data['to_custom'] if kwargs['commit']: diff --git a/ietf/secr/announcement/views.py b/ietf/secr/announcement/views.py index 8d6ba084c..c10ac692b 100644 --- a/ietf/secr/announcement/views.py +++ b/ietf/secr/announcement/views.py @@ -6,7 +6,6 @@ from django.template import RequestContext from ietf.ietfauth.decorators import has_role from ietf.utils.mail import send_mail_text -from ietf.wgchairs.accounts import get_person_for_user from ietf.group.models import Group from ietf.secr.utils.group import current_nomcom from ietf.secr.utils.decorators import check_for_cancel @@ -104,4 +103,4 @@ def confirm(request): 'message': data, 'to': to}, RequestContext(request, {}), - ) \ No newline at end of file + ) From 81ba5ef9593db0de79f55583b366f07f79229f8c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 19 Sep 2013 11:04:31 +0000 Subject: [PATCH 017/173] Move delegate handling to group edit page (like other group personnel), show group delegates in the charter page with the other personnel - Legacy-Id: 6195 --- ietf/templates/wginfo/group_charter.html | 11 +++++++++++ ietf/wginfo/edit.py | 25 +++++++++++++++++------- ietf/wginfo/tests.py | 2 ++ ietf/wginfo/urls.py | 1 - ietf/wginfo/views.py | 2 +- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/ietf/templates/wginfo/group_charter.html b/ietf/templates/wginfo/group_charter.html index 7175748a3..b29f6a918 100644 --- a/ietf/templates/wginfo/group_charter.html +++ b/ietf/templates/wginfo/group_charter.html @@ -105,6 +105,17 @@ is occasionally incorrect. {% endif %} + {% if group.delegates %} + + Delegate{{ group.delegates|pluralize }}: + + {% for delegate in group.delegates %} + {{ delegate.person.plain_name }} <{{ delegate.address }}>
+ {% endfor %} + + + {% endif %} + Mailing List Address:{{ group.list_email|urlize }} diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index 068a62b10..92009b0b0 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -23,13 +23,16 @@ from ietf.wgcharter.mails import email_secretariat from ietf.person.forms import EmailsField from ietf.doc.utils import get_tags_for_stream_id +MAX_GROUP_DELEGATES = 3 + class WGForm(forms.Form): - name = forms.CharField(max_length=255, label="WG Name", required=True) - acronym = forms.CharField(max_length=10, label="WG Acronym", required=True) - state = forms.ModelChoiceField(GroupStateName.objects.all(), label="WG State", required=True) - chairs = EmailsField(label="WG Chairs", required=False) - secretaries = EmailsField(label="WG Secretaries", required=False) - techadv = EmailsField(label="WG Technical Advisors", required=False) + name = forms.CharField(max_length=255, label="Name", required=True) + acronym = forms.CharField(max_length=10, label="Acronym", required=True) + state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True) + chairs = EmailsField(label="Chairs", required=False) + secretaries = EmailsField(label="Secretaries", required=False) + techadv = EmailsField(label="Technical Advisors", required=False) + delegates = EmailsField(label="Delegates", required=False, help_text=mark_safe("Type in name to search for person
Chairs can delegate the authority to update the state of group documents - max %s persons at a given time" % MAX_GROUP_DELEGATES)) ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'), label="Shepherding AD", empty_label="(None)", required=False) parent = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), label="IETF Area", empty_label="(None)", required=False) list_email = forms.CharField(max_length=64, required=False) @@ -97,6 +100,13 @@ class WGForm(forms.Form): def clean_urls(self): return [x.strip() for x in self.cleaned_data["urls"].splitlines() if x.strip()] + def clean_delegates(self): + if len(self.cleaned_data["delegates"]) > MAX_GROUP_DELEGATES: + raise forms.ValidationError("At most %s delegates can be appointed at the same time, please remove %s delegates." % ( + MAX_GROUP_DELEGATES, len(self.cleaned_data["delegates"]) - MAX_GROUP_DELEGATES)) + return self.cleaned_data["delegates"] + + def format_urls(urls, fs="\n"): res = [] for u in urls: @@ -221,7 +231,7 @@ def edit(request, acronym=None, action="edit"): shutil.copy(old, new) # update roles - for attr, slug, title in [('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors")]: + for attr, slug, title in [('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors"), ('delegates', 'delegate', "Delegates")]: new = clean[attr] old = Email.objects.filter(role__group=wg, role__name=slug).select_related("person") if set(new) != set(old): @@ -269,6 +279,7 @@ def edit(request, acronym=None, action="edit"): chairs=Email.objects.filter(role__group=wg, role__name="chair"), secretaries=Email.objects.filter(role__group=wg, role__name="secr"), techadv=Email.objects.filter(role__group=wg, role__name="techadv"), + delegates=Email.objects.filter(role__group=wg, role__name="delegate"), ad=wg.ad_id if wg.ad else None, parent=wg.parent.id if wg.parent else None, list_email=wg.list_email if wg.list_email else None, diff --git a/ietf/wginfo/tests.py b/ietf/wginfo/tests.py index 7615e23bc..33134b233 100644 --- a/ietf/wginfo/tests.py +++ b/ietf/wginfo/tests.py @@ -355,6 +355,7 @@ class WgEditTestCase(django.test.TestCase): chairs="aread@ietf.org, ad1@ietf.org", secretaries="aread@ietf.org, ad1@ietf.org, ad2@ietf.org", techadv="aread@ietf.org", + delegates="ad2@ietf.org", list_email="mars@mail", list_subscribe="subscribe.mars", list_archive="archive.mars", @@ -375,6 +376,7 @@ class WgEditTestCase(django.test.TestCase): self.assertEquals(group.ad, ad) for k in ("chair", "secr", "techadv"): self.assertTrue(group.role_set.filter(name=k, email__address="aread@ietf.org")) + self.assertTrue(group.role_set.filter(name="delegate", email__address="ad2@ietf.org")) self.assertEquals(group.list_email, "mars@mail") self.assertEquals(group.list_subscribe, "subscribe.mars") self.assertEquals(group.list_archive, "archive.mars") diff --git a/ietf/wginfo/urls.py b/ietf/wginfo/urls.py index bad932b21..b0106f144 100644 --- a/ietf/wginfo/urls.py +++ b/ietf/wginfo/urls.py @@ -30,5 +30,4 @@ urlpatterns = patterns('', (r'^(?P[a-zA-Z0-9-]+)/milestones/charter/reset/$', milestones.reset_charter_milestones, None, "wg_reset_charter_milestones"), (r'^(?P[a-zA-Z0-9-]+)/ajax/searchdocs/$', milestones.ajax_search_docs, None, "wg_ajax_search_docs"), (r'^(?P[a-zA-Z0-9-]+)/workflow/$', edit.customize_workflow), - (r'^(?P[^/]+)/management/', include('ietf.wgchairs.urls')), ) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 87f4d8caf..27871eb8b 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -255,6 +255,7 @@ def group_charter(request, acronym): group = get_object_or_404(Group, type="wg", acronym=acronym) fill_in_charter_info(group, include_drafts=False) + group.delegates = Email.objects.filter(role__group=group, role__name="delegate") actions = [] if group.state_id != "conclude": @@ -269,7 +270,6 @@ def group_charter(request, acronym): is_chair = request.user.is_authenticated() and group.role_set.filter(name="chair", person__user=request.user) if is_chair or has_role(request.user, "Secretariat"): - actions.append((u"Manage delegates", urlreverse("manage_delegates", kwargs=dict(acronym=group.acronym)))) actions.append((u"Customize workflow", urlreverse("ietf.wginfo.edit.customize_workflow", kwargs=dict(acronym=group.acronym)))) return render_to_response('wginfo/group_charter.html', From 8ffecd80158dd25a33f21bc705f7efff884d7090 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 19 Sep 2013 11:05:21 +0000 Subject: [PATCH 018/173] Clean up a couple of old-school tags in group edit template, add link to account creation for personnel - Legacy-Id: 6196 --- ietf/templates/wginfo/edit.html | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/ietf/templates/wginfo/edit.html b/ietf/templates/wginfo/edit.html index 2ca32f710..8af9917b8 100644 --- a/ietf/templates/wginfo/edit.html +++ b/ietf/templates/wginfo/edit.html @@ -9,6 +9,7 @@ Start chartering new WG {% endblock %} {% block morecss %} +form.edit td { padding-bottom: .5em; } form.edit #id_name, form.edit #id_list_email, form.edit #id_list_subscribe, @@ -16,8 +17,7 @@ form.edit #id_list_archive, form.edit #id_urls, form.edit #id_comments { width: 400px; } form.edit input[type=checkbox] { vertical-align: middle; } -ul.errorlist { border-width: 0px; padding: 0px; margin: 0px;} -ul.errorlist li { color: #a00; margin: 0px; padding: 0px; list-style: none; } +form.edit #id_urls { height: 4em; } {% endblock %} {% block pagehead %} @@ -27,28 +27,30 @@ ul.errorlist li { color: #a00; margin: 0px; padding: 0px; list-style: none; } {% block content %} {% load ietf_filters %}

-{% ifequal action "edit" %} +{% if action == "edit" %} Edit WG {{ wg.acronym }} {% else %} - {% ifequal action "charter" %} + {% if action == "charter" %} Start chartering new WG {% else %} Create new WG or BoF - {% endifequal %} -{% endifequal %} + {% endif %} +{% endif %}

+

Note that persons with authorization to manage information, e.g. +chairs and delegates, need a Datatracker account to actually do +so. New accounts can be created here.

+ {% for field in form.visible_fields %} @@ -65,16 +67,16 @@ Create new WG or BoF
{{ field.label_tag }}: {% if field.field.required %}*{% endif %} {{ field }} - {% ifequal field.name "ad" %} - {% if user|has_role:"Area Director" %} + {% if field.name == "ad" and user|has_role:"Area Director" %} {% endif %} - {% endifequal %} {% if field.help_text %}
{{ field.help_text }}
{% endif %} {{ field.errors }}
- {% ifequal action "edit" %} - Back - + {% if action == "edit" %} + Cancel + {% else %} - {% ifequal action "charter" %} + {% if action == "charter" %} {% else %} - {% endifequal %} - {% endifequal %} + {% endif %} + {% endif %}
From 19fc7c14bcd86e29f34d21ba4778f6881f24ef20 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 19 Sep 2013 15:19:12 +0000 Subject: [PATCH 019/173] Increase contrast a bit for the customize buttons - Legacy-Id: 6197 --- ietf/templates/wginfo/customize_workflow.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/templates/wginfo/customize_workflow.html b/ietf/templates/wginfo/customize_workflow.html index 082ecfe3d..147267aaf 100644 --- a/ietf/templates/wginfo/customize_workflow.html +++ b/ietf/templates/wginfo/customize_workflow.html @@ -12,7 +12,7 @@ .state-table .set-state input, .set-tag input { width: 6em; } .state-table .toggled { display: none; } .state-table .toggler { - color: #666; + color: #444; background: #ddd; font-weight: bold; text-decoration: none; @@ -23,7 +23,7 @@ margin-bottom: 0.5em; } .state-table .inactive .toggler { color: #aaa; background: #eee; } -.state-table .toggler:hover { background-color: #999; color: #fff; } +.state-table .toggler:hover { color: #fff; background-color: #999; } {% endblock %} {% block content %} From a871d6e19e647a072a20201bac812ac07860e1b5 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 19 Sep 2013 15:22:59 +0000 Subject: [PATCH 020/173] Remove wgchairs app, all of its functionality is now superceded by wginfo/ and other recent developments in doc/ - Legacy-Id: 6198 --- bin/mkdiagram | 2 +- ietf/doc/proxy.py | 5 - ietf/group/proxy.py | 16 +- ietf/settings.py | 1 - .../wgchairs/confirm_management_writeup.html | 55 -- ietf/templates/wgchairs/draft_state.html | 1 - .../wgchairs/edit_management_shepherd.html | 41 -- .../wgchairs/edit_management_writeup.html | 67 --- ietf/templates/wgchairs/manage_delegates.html | 60 --- ietf/templates/wgchairs/manage_workflow.html | 165 ------ ietf/templates/wgchairs/notexistdelegate.html | 28 - .../notexistsdelegate_delegate_email.txt | 10 - .../notexistsdelegate_secretariat_email.txt | 15 - .../notexistsdelegate_wgchairs_email.txt | 12 - .../wgchairs/shepherd_document_row.html | 17 - .../wgchairs/wg_shepherd_documents.html | 122 ----- .../wgchairs/wgchairs_admin_options.html | 23 - ietf/templates/wginfo/wg_base.html | 83 --- ietf/wgchairs/.gitignore | 1 - ietf/wgchairs/__init__.py | 18 - ietf/wgchairs/accounts.py | 108 ---- ietf/wgchairs/forms.py | 478 ------------------ ietf/wgchairs/migrations/.gitignore | 1 - ietf/wgchairs/migrations/0001_initial.py | 110 ---- ietf/wgchairs/migrations/0002_add_writeup.py | 163 ------ ietf/wgchairs/migrations/__init__.py | 0 ietf/wgchairs/models.py | 87 ---- ietf/wgchairs/templatetags/.gitignore | 1 - ietf/wgchairs/templatetags/__init__.py | 0 ietf/wgchairs/templatetags/wgchairs_tags.py | 47 -- ietf/wgchairs/tests.py | 214 -------- ietf/wgchairs/urls.py | 7 - ietf/wgchairs/views.py | 73 --- 33 files changed, 15 insertions(+), 2016 deletions(-) delete mode 100644 ietf/templates/wgchairs/confirm_management_writeup.html delete mode 100644 ietf/templates/wgchairs/draft_state.html delete mode 100644 ietf/templates/wgchairs/edit_management_shepherd.html delete mode 100644 ietf/templates/wgchairs/edit_management_writeup.html delete mode 100644 ietf/templates/wgchairs/manage_delegates.html delete mode 100644 ietf/templates/wgchairs/manage_workflow.html delete mode 100644 ietf/templates/wgchairs/notexistdelegate.html delete mode 100644 ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt delete mode 100644 ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt delete mode 100644 ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt delete mode 100644 ietf/templates/wgchairs/shepherd_document_row.html delete mode 100644 ietf/templates/wgchairs/wg_shepherd_documents.html delete mode 100644 ietf/templates/wgchairs/wgchairs_admin_options.html delete mode 100644 ietf/templates/wginfo/wg_base.html delete mode 100644 ietf/wgchairs/.gitignore delete mode 100644 ietf/wgchairs/__init__.py delete mode 100644 ietf/wgchairs/accounts.py delete mode 100644 ietf/wgchairs/forms.py delete mode 100644 ietf/wgchairs/migrations/.gitignore delete mode 100644 ietf/wgchairs/migrations/0001_initial.py delete mode 100644 ietf/wgchairs/migrations/0002_add_writeup.py delete mode 100644 ietf/wgchairs/migrations/__init__.py delete mode 100644 ietf/wgchairs/models.py delete mode 100644 ietf/wgchairs/templatetags/.gitignore delete mode 100644 ietf/wgchairs/templatetags/__init__.py delete mode 100644 ietf/wgchairs/templatetags/wgchairs_tags.py delete mode 100644 ietf/wgchairs/tests.py delete mode 100644 ietf/wgchairs/urls.py delete mode 100644 ietf/wgchairs/views.py diff --git a/bin/mkdiagram b/bin/mkdiagram index 765a7ba52..5eca0efc1 100755 --- a/bin/mkdiagram +++ b/bin/mkdiagram @@ -9,7 +9,7 @@ trap 'echo "$program($LINENO): Command failed with error code $? ($0 $*)"; exit if [ "$*" ]; then apps="$@"; graph="${1%.*}"; else apps=$(ls */models.py | sed 's!/models.py!!'); graph="models"; fi newapps="doc group meeting message person name" -legacyapps="announcements idindex idrfc idtracker iesg ietfauth ietfworkflows ipr liaisons mailinglists proceedings redirects submit wgchairs wgcharter wginfo" +legacyapps="announcements idindex idrfc idtracker iesg ietfauth ietfworkflows ipr liaisons mailinglists proceedings redirects submit wgcharter wginfo" proxy="$(grep ^class */proxy.py | tr '()' ' ' | awk '{printf $2 ","}')" names="$(grep ^class name/models.py | tr '()' ' ' | awk '{printf $2 ","}')" diff --git a/ietf/doc/proxy.py b/ietf/doc/proxy.py index 21993174c..6b2fbe15c 100644 --- a/ietf/doc/proxy.py +++ b/ietf/doc/proxy.py @@ -194,11 +194,6 @@ class InternetDraft(Document): def authors(self): return IDAuthor.objects.filter(document=self) - @property - def protowriteup_set(self): - from ietf.wgchairs.models import ProtoWriteUpProxy - return ProtoWriteUpProxy.objects.filter(doc=self, type="changed_protocol_writeup") - # methods from InternetDraft def displayname(self): return self.name diff --git a/ietf/group/proxy.py b/ietf/group/proxy.py index 2ab006760..5245eb0cc 100644 --- a/ietf/group/proxy.py +++ b/ietf/group/proxy.py @@ -94,6 +94,19 @@ def proxied_role_emails(emails): proxy_role_email(e) return emails +class WGDelegateProxy(Role): + #person = models.ForeignKey(PersonOrOrgInfo) # same name + #wg = models.ForeignKey(IETFWG) + @property + def wg(self): + return self.group + + def __unicode__(self): + return u"%s" % self.person + + class Meta: + proxy = True + class IETFWG(Group): objects = TranslatingManager(dict(group_acronym="id", group_acronym__acronym="acronym", @@ -211,8 +224,7 @@ class IETFWG(Group): return d @property def wgdelegate_set(self): - from ietf.wgchairs.models import WGDelegate - return WGDelegate.objects.filter(group=self, name="delegate") + return WGDelegateProxy.objects.filter(group=self, name="delegate") class Meta: proxy = True diff --git a/ietf/settings.py b/ietf/settings.py index d3b7367cc..ad5f1069b 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -175,7 +175,6 @@ INSTALLED_APPS = ( 'ietf.wginfo', 'ietf.submit', 'ietf.ietfworkflows', - 'ietf.wgchairs', 'ietf.wgcharter', 'ietf.sync', 'ietf.community', diff --git a/ietf/templates/wgchairs/confirm_management_writeup.html b/ietf/templates/wgchairs/confirm_management_writeup.html deleted file mode 100644 index 3bc5ceab9..000000000 --- a/ietf/templates/wgchairs/confirm_management_writeup.html +++ /dev/null @@ -1,55 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% load ietf_filters wgchairs_tags %} - -{% block title %}Change shepherd write-up for {{ doc }}{% endblock %} - -{% block wg_content %} - -

-Return to shepherd list -

- -

Updating write-up for {{ doc }}

- -

-Before you modify the shepherd write-up please revise the 'Doc Shepherd Follow-up Underway' annotation tag and set or reset it if appropriate. -

-

-Remember that you must provide a comment if you change the annotation tag state. -

- - -
- - -{% if form.message %} - -{% endif %} - - -
Doc Shepherd Follow-up Underway
- {{ form.message.value }} -
- - - - -
-{{ form.comment }} -
-

- Change write-up and ...
- -
- Cancel, I don't want to do any change! -

- -
- - - -
New shepherd write-up
{{ form.get_writeup|linebreaksbr }}
- -
- -{% endblock %} diff --git a/ietf/templates/wgchairs/draft_state.html b/ietf/templates/wgchairs/draft_state.html deleted file mode 100644 index 956ebd44f..000000000 --- a/ietf/templates/wgchairs/draft_state.html +++ /dev/null @@ -1 +0,0 @@ -{{ state.name }} diff --git a/ietf/templates/wgchairs/edit_management_shepherd.html b/ietf/templates/wgchairs/edit_management_shepherd.html deleted file mode 100644 index b9a9e616a..000000000 --- a/ietf/templates/wgchairs/edit_management_shepherd.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% load ietf_filters %} - -{% block title %}Change shepherd for {{ doc }}{% endblock %} - -{% block wg_content %} - -

-Return to shepherd list -Return to {{ doc }} -

- -

Change shepherd for {{ doc }}

- -
-
- - - -
Actual shepherd
{% if doc.shepherd %}{{ doc.shepherd }}{% else %}No shepherd assigned{% endif %}
- - - -
-
-
- - -{% if form.message %} - -{% endif %} - - -
Change shepherd
- {{ form.message.value }} -
{{ form.as_p }}
- - {% if form.can_cancel %}No! I don't want to continue{% endif %} -
-
-{% endblock %} diff --git a/ietf/templates/wgchairs/edit_management_writeup.html b/ietf/templates/wgchairs/edit_management_writeup.html deleted file mode 100644 index 7a61437c0..000000000 --- a/ietf/templates/wgchairs/edit_management_writeup.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% load ietf_filters wgchairs_tags %} - -{% block title %} {% if can_edit %} Change {% else %} View {% endif %} shepherd write-up for {{ doc }}{% endblock %} - -{% block wg_content %} - -

-Return to shepherd list -

- -

{% if can_edit %} Change {% else %} View {% endif %} shepherd write-up for {{ doc }}

- - - - -
Draft stateActual shepherd write-upLast updated
{% show_state doc %}{{ writeup.writeup|linebreaksbr }}{% if writeup %}{{ writeup.date }} by {{ writeup.person }}{% endif %}
- -

-Please, note that the 'Doc Shepherd Follow-up Underway' annotation tag is {% if not followup %}NOT{% endif %} set for {{ doc }}. -

- -{% if can_edit %} -
-
- - -{% if form.message %} - -{% endif %} - - -
Edit shepherd write-up
- {{ form.message.value }} -
-
- -
- -
- -
- - - -
Upload a new shepherd write-up
-

Replace the current write-up with the contents of a plain ascii file:

-
- -
-
-{% else %} -{% if authorized_user %} - - - -
Edit shepherd write-up
-

- You can not edit or upload the shepherd write-up for {{ doc }} because the draft is not on "WG Consensus: Waiting for Write-Up" state. -

-

- Please contact with the {{ wg }} Working Group chair. -

-
-{% endif %} -{% endif %} -{% endblock %} diff --git a/ietf/templates/wgchairs/manage_delegates.html b/ietf/templates/wgchairs/manage_delegates.html deleted file mode 100644 index f1f2eaf58..000000000 --- a/ietf/templates/wgchairs/manage_delegates.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "wginfo/wg_base.html" %} - - -{% block wg_titledetail %}Delegates{% endblock %} - -{% block wg_content %} -
- -

Manage delegates

-

-Sometimes, a WG has one (or more) WG Secretaries, in addition to the WG Chairs. -This page lets the WG Chairs delegate the authority to do updates to the WG state of WG documents in the datatracker. -

- -

-You may at most delegate the datatracker update rights to {{ max_delegates }} persons at any given time. -

- - - - -
-{% if delegates %} -
- - - {% for delegate in delegates %} - - {% endfor %} -
RemoveDelegate name
{{ delegate.person }}
- -
-{% else %} -No delegates -{% endif %} -
- - -{% if add_form.message %} - -{% endif %} - -
Add new delegate
- {{ add_form.message.value }} -
-{% if can_add %} -
- {{ add_form.as_p }} -

- - {% if add_form.can_cancel %}No! I don't want to continue{% endif %} -

-
-{% else %} -You can only assign {{ max_delegates }} delegates. Please remove delegates to add a new one. -{% endif %} -
-
-
-{% endblock %} diff --git a/ietf/templates/wgchairs/manage_workflow.html b/ietf/templates/wgchairs/manage_workflow.html deleted file mode 100644 index bc5a4fbd7..000000000 --- a/ietf/templates/wgchairs/manage_workflow.html +++ /dev/null @@ -1,165 +0,0 @@ -{% extends "wginfo/wg_base.html" %} - -{% block wg_titledetail %}Manage Workflow{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock pagehead %} - -{% block wg_content %} -
-

Edit workflow

-
- - -
-
-
- - - - - {% for state in states %} - - - -{% endfor %} -
States used in {{ wg }} Working Group
{{ state.name }}

- - - - - {% for tag in tags %} - - - -{% endfor %} -
Annotation tags used in {{ wg }} Working Group
{{ tag.name }}
-
- - - - - {% for transition in workflow.transitions.all %} - - - - - - {% endfor %} - {% if not workflow.transitions.all.count %} - - {% endif %} -
Transition nameInitial statesDestination state
- {{ transition.name }} - - {% for state in transition.states.all %} - {{state.name }}{% if not forloop.last %}
{% endif %} - {% endfor %} -
- {{ transition.destination.name }} -
There are no transitions defined so any state change is allowed
-
-
- -
-

-Please note that the states you can not uncheck are needed in all IETF WGs. -

-

-You can see the default Working Group I-D State Diagram in Section 4.1 of RFC6174 -

-
- - - - - {% for state in default_states %} - - - - - -{% endfor %} -
Used in {{ wg }}Available statesDefinition
-
-
[+] {{ state.statedescription_set.all.0.definition|safe }}
-
-
- -
-
- -
-

-You can see the default Working Group I-D State Diagram in Section 4.1 of RFC6174 -

-
- - - - -{{ formset.as_table }} -
DeleteTransition nameInitial statesDestination state
- -
-
- -
-
- - - - - {% for tag in default_tags %} - - - - -{% endfor %} -
Used in {{ wg }}Available annotation tags
- -
-
-
-
- - - -{% endblock %} diff --git a/ietf/templates/wgchairs/notexistdelegate.html b/ietf/templates/wgchairs/notexistdelegate.html deleted file mode 100644 index 706907022..000000000 --- a/ietf/templates/wgchairs/notexistdelegate.html +++ /dev/null @@ -1,28 +0,0 @@ -{% if shepherd %} -

-The shepherd you are trying to designate does not have a personal user-id and password to log-on to the Datatracker. -

-

-An email will be sent to the following addresses to inform that -the person you have designated to be one of your document shepherds -currently does not have login credentials for the Datatracker -and should contact the Secretariat to obtain their own user-id and -password for the Datatracker. -

-{% else %} -

-The delegate you are trying to designate does not have a personal user-id and password to log-on to the Datatracker. -

-

-An email will be sent to the following addresses to inform that -the person you have designated to be one of your delegates -currently does not have login credentials for the Datatracker -and should contact the Secretariat to obtain their own user-id and -password for the Datatracker. -

-{% endif %} -
    -{% for email in email_list %} -
  • {{ email }}
  • -{% endfor %} -
diff --git a/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt b/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt deleted file mode 100644 index de2318a93..000000000 --- a/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt +++ /dev/null @@ -1,10 +0,0 @@ -{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add you as a {{ wg }} {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. - -You don't have an user/password to log into the datatracker so you must contact -the Secretariat at iesg-secretary@ietf.org in order to get your credentials. - -When you get your credentials, please inform {{ chair }} at -{{ chair.email.1 }} so he/she can finish the designate process. - -Thank you. -{% endautoescape %} diff --git a/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt b/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt deleted file mode 100644 index 27edacc11..000000000 --- a/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt +++ /dev/null @@ -1,15 +0,0 @@ -{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add a person with email -{{ delegate_email }} as a {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. - -This person don't have an user/password to log into the datatracker so -an email has been seent to {{ delegate_email }} in order to he/she contacs the -Secretariat to request his/her credentials. - -{% if delegate_persons %} -Please, note that the following persons with {{ delegate_email }} email address -already exists in the system but they can not log in. -{% for person in delegate_persons %} -{{ person.pk }} - {{ person }} -{% endfor %} -{% endif %} -{% endautoescape %} \ No newline at end of file diff --git a/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt b/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt deleted file mode 100644 index 477111dcf..000000000 --- a/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt +++ /dev/null @@ -1,12 +0,0 @@ -{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add a person with email -{{ delegate_email }} as a {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. - -This person don't have an user/password to log into the datatracker so -an email has been seent to {{ delegate_email }} in order to he/she contacs the -Secretariat to request his/her credentials. - -When he/she gets her credentials then he/she will send an email to -{{ chair }} at {{ chair.email.1 }}. - -{{ chair }} could then assign this person as {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. -{% endautoescape%} \ No newline at end of file diff --git a/ietf/templates/wgchairs/shepherd_document_row.html b/ietf/templates/wgchairs/shepherd_document_row.html deleted file mode 100644 index a58e1eac5..000000000 --- a/ietf/templates/wgchairs/shepherd_document_row.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load wgchairs_tags %} - - - - {{ doc.title }} - - - Change shepherd - - - {% writeup doc %} - [Edit] - - - {% writeupdate doc %} - - diff --git a/ietf/templates/wgchairs/wg_shepherd_documents.html b/ietf/templates/wgchairs/wg_shepherd_documents.html deleted file mode 100644 index 2afcd9bac..000000000 --- a/ietf/templates/wgchairs/wg_shepherd_documents.html +++ /dev/null @@ -1,122 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% comment %} -Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% block wg_titledetail %}Documents{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock pagehead %} - -{% block wg_content %} - -

Documents by its shepherd

-
- - -
-
- - - - - - - - - {% for doc in no_shepherd %} - {% include "wgchairs/shepherd_document_row.html" %} - {% endfor %} - -
DocumentChange shepherdShepherd write-upShepherd write-up last update
-
- -
- - - - - - - - - {% for doc in my_documents %} - {% include "wgchairs/shepherd_document_row.html" %} - {% endfor %} - -
DocumentStatusShepherd write-upShepherd write-up last update
-
- -
-{% regroup other_shepherds by shepherd as regrouped %} -{% for documents in regrouped %} -

{{ documents.grouper }}

- - - - - - - - {% for doc in documents.list %} - {% include "wgchairs/shepherd_document_row.html" %} - {% endfor %} -
DocumentStatusShepherd write-upShepherd write-up last update
-{% endfor %} -
- - -{% endblock wg_content %} - diff --git a/ietf/templates/wgchairs/wgchairs_admin_options.html b/ietf/templates/wgchairs/wgchairs_admin_options.html deleted file mode 100644 index b8c45a00f..000000000 --- a/ietf/templates/wgchairs/wgchairs_admin_options.html +++ /dev/null @@ -1,23 +0,0 @@ -{% if can_manage_workflow %} - {% ifequal selected "manage_workflow" %} - Manage workflow - {% else %} - Manage workflow - {% endifequal %} | -{% endif %} - -{% if can_manage_delegates %} - {% ifequal selected "manage_delegates" %} - Manage delegations - {% else %} - Manage delegations - {% endifequal %} | -{% endif %} - -{% if can_manage_shepherds %} - {% ifequal selected "manage_shepherds" %} - Manage shepherds - {% else %} - Manage shepherds - {% endifequal %} | -{% endif %} diff --git a/ietf/templates/wginfo/wg_base.html b/ietf/templates/wginfo/wg_base.html deleted file mode 100644 index 4f73e3fe4..000000000 --- a/ietf/templates/wginfo/wg_base.html +++ /dev/null @@ -1,83 +0,0 @@ -{% extends "base.html" %} -{% comment %} -Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% load ietf_filters wgchairs_tags %} -{% block title %}{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}) - {% block wg_titledetail %}{% endblock %}{% endblock %} - -{% block morecss %} -.ietf-navset { - background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px; - color:white; - border:1px solid black; - padding:4px; -} -.ietf-navset .selected { font-weight:bold; padding: 0 3px; } -.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } - -.ietf-wg-details { float:right; padding: 4px;margin-top:16px; margin-left: 16px; } -.ietf-wg-details tr { vertical-align: top; } -.ietf-concluded-bg {background-color: #F8F8D0; } -.ietf-concluded-warning { background:red;color:white;padding:2px 2px;} -.ietf-proposed-bg { } -.ietf-proposed-warning { background:green;color:white;padding:2px 2px;} -.ietf-box th { - font-weight: bold; - padding-top: 1em; - text-align: left; -} -.ietf-box tr:first-child th { - padding-top: 0; -} -{% endblock morecss %} - -{% block content %} -
-

{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}){% if concluded %}
(concluded WG){% endif %}{% if proposed %}
(proposed WG){% endif %}

- -
-{% ifequal selected "documents" %}Documents{% else %}Documents{% endifequal %} | -{% ifequal selected "charter" %}Charter{% else %}Charter{% endifequal %} | -{% wgchairs_admin_options wg %} -History | -{% if wg.clean_email_archive|startswith:"http:" or wg.clean_email_archive|startswith:"https:" or wg.clean_email_archive|startswith:"ftp:" %} -List Archive » | -{% endif %} -Tools WG Page » -
- -{% block wg_content %} -{% endblock wg_content %} - -
-{% endblock content %} diff --git a/ietf/wgchairs/.gitignore b/ietf/wgchairs/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/wgchairs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/wgchairs/__init__.py b/ietf/wgchairs/__init__.py deleted file mode 100644 index e8d53c9a3..000000000 --- a/ietf/wgchairs/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# coding: latin-1 - -from types import ModuleType -import urls, models, views, forms, accounts - -# These people will be sent a stack trace if there's an uncaught exception in -# code any of the modules imported above: -DEBUG_EMAILS = [ - ('Emilio A. Sánchez', 'esanchez@yaco.es'), -] - -for k in locals().keys(): - m = locals()[k] - if isinstance(m, ModuleType): - if hasattr(m, "DEBUG_EMAILS"): - DEBUG_EMAILS += list(getattr(m, "DEBUG_EMAILS")) - setattr(m, "DEBUG_EMAILS", DEBUG_EMAILS) - diff --git a/ietf/wgchairs/accounts.py b/ietf/wgchairs/accounts.py deleted file mode 100644 index c6b5b7645..000000000 --- a/ietf/wgchairs/accounts.py +++ /dev/null @@ -1,108 +0,0 @@ -from django.conf import settings -from ietf.group.models import Role - -def is_secretariat(user): - if not user or not user.is_authenticated(): - return False - return bool(user.groups.filter(name='Secretariat')) - - -def is_area_director_for_group(person, group): - return bool(group.area.area.areadirector_set.filter(person=person).count()) - -def is_area_director_for_groupREDESIGN(person, group): - return bool(Role.objects.filter(group=group.parent, person=person, name__in=("ad", "pre-ad"))) - - -def is_group_chair(person, group): - if group.chairs().filter(person=person): - return True - return False - -def is_group_chairREDESIGN(person, group): - return bool(Role.objects.filter(group=group, person=person, name="chair")) - - -def is_group_delegate(person, group): - return bool(group.wgdelegate_set.filter(person=person).count()) - -def is_group_delegateREDESIGN(person, group): - return bool(Role.objects.filter(group=group, person=person, name="delegate")) - - -def get_person_for_user(user): - try: - return user.get_profile().person() - except: - return None - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.accounts import is_secretariat, get_person_for_user - is_area_director_for_group = is_area_director_for_groupREDESIGN - is_group_chair = is_group_chairREDESIGN - is_group_delegate = is_group_delegateREDESIGN - - -def can_do_wg_workflow_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_do_wg_workflow_in_document(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - return (is_secretariat(user) or can_do_wg_workflow_in_group(document.group.ietfwg)) - - -def can_manage_workflow_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_manage_delegates_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_manage_shepherds_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_manage_shepherd_of_a_document(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - return can_manage_shepherds_in_group(user, document.group.ietfwg) - - -def can_manage_writeup_of_a_document_no_state(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - group = document.group.ietfwg - return (is_secretariat(user) or - is_group_chair(person, group) or - is_area_director_for_group(person, group) or - is_group_delegate(person, group)) - - -def can_manage_writeup_of_a_document(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - return (can_manage_writeup_of_a_document_no_state(user, document) or - person == document.shepherd) - - - diff --git a/ietf/wgchairs/forms.py b/ietf/wgchairs/forms.py deleted file mode 100644 index 001acec7c..000000000 --- a/ietf/wgchairs/forms.py +++ /dev/null @@ -1,478 +0,0 @@ -import datetime - -from django import forms -from django.conf import settings -from django.db.models import Q -from django.forms.models import BaseModelFormSet -from django.template.loader import render_to_string -from django.utils.safestring import mark_safe - -from ietf.wgchairs.models import WGDelegate, ProtoWriteUp -from ietf.wgchairs.accounts import get_person_for_user -from ietf.ietfworkflows.constants import REQUIRED_STATES -from ietf.ietfworkflows.utils import (get_default_workflow_for_wg, get_workflow_for_wg, - update_tags, FOLLOWUP_TAG, get_state_by_name) -from ietf.ietfworkflows.models import AnnotationTag, State -from ietf.idtracker.models import PersonOrOrgInfo -from ietf.utils.mail import send_mail_text - -from workflows.models import Transition - -from ietf.doc.models import WriteupDocEvent -from ietf.person.models import Person, Email -from ietf.group.models import Group, Role, RoleName -from ietf.group.utils import save_group_in_history -from ietf.name.models import DocTagName - - -class RelatedWGForm(forms.Form): - - can_cancel = False - - def __init__(self, *args, **kwargs): - self.wg = kwargs.pop('wg', None) - self.user = kwargs.pop('user', None) - self.message = {} - super(RelatedWGForm, self).__init__(*args, **kwargs) - - def get_message(self): - return self.message - - def set_message(self, msg_type, msg_value): - self.message = {'type': msg_type, - 'value': msg_value, - } - - -class TagForm(RelatedWGForm): - - tags = forms.ModelMultipleChoiceField(AnnotationTag.objects.filter(workflow__name='Default WG Workflow'), - widget=forms.CheckboxSelectMultiple, required=False) - - def save(self): - workflow = get_workflow_for_wg(self.wg) - workflow.selected_tags.clear() - for tag in self.cleaned_data['tags']: - workflow.selected_tags.add(tag) - return workflow - - -class StateForm(RelatedWGForm): - - states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow'), - widget=forms.CheckboxSelectMultiple, required=False) - - def update_transitions(self, workflow): - for transition in workflow.transitions.all(): - if not workflow.selected_states.filter(pk=transition.destination.pk).count(): - transition.delete() - continue - for state in transition.states.all(): - if not workflow.selected_states.filter(pk=state.pk).count(): - transition.states.remove(state) - if not transition.states.count(): - transition.delete() - continue - - def save(self): - workflow = get_workflow_for_wg(self.wg) - workflow.selected_states.clear() - for state in self.cleaned_data['states']: - workflow.selected_states.add(state) - for name in REQUIRED_STATES: - rstate = get_state_by_name(name) - if rstate: - workflow.selected_states.add(rstate) - self.update_transitions(workflow) - return workflow - - -class DeleteTransitionForm(RelatedWGForm): - - transitions = forms.ModelMultipleChoiceField(Transition.objects.all(), - widget=forms.CheckboxSelectMultiple) - - def __init__(self, *args, **kwargs): - super(DeleteTransitionForm, self).__init__(*args, **kwargs) - workflow = get_workflow_for_wg(self.wg) - self.fields['transitions'].queryset = self.fields['transitions'].queryset.filter(workflow=workflow) - - def save(self): - for transition in self.cleaned_data['transitions']: - transition.delete() - - -class TransitionForm(forms.ModelForm): - - states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow')) - - class Meta: - model = Transition - fields = ('name', 'states', 'destination', ) - - def __init__(self, *args, **kwargs): - self.wg = kwargs.pop('wg', None) - self.user = kwargs.pop('user', None) - super(TransitionForm, self).__init__(*args, **kwargs) - workflow = get_workflow_for_wg(self.wg) - self.fields['states'].queryset = workflow.selected_states.all() - self.fields['destination'].queryset = workflow.selected_states.all() - self.fields['destination'].required = True - if self.instance.pk: - self.fields['states'].initial = [i.pk for i in self.instance.states.all()] - self.instance.workflow = workflow - - def as_row(self): - return self._html_output(u'%(errors)s%(field)s%(help_text)s', u'%s', '', u'
%s', False) - - def save(self, *args, **kwargs): - instance = super(TransitionForm, self).save(*args, **kwargs) - for state in self.cleaned_data['states']: - state.transitions.add(instance) - - -class TransitionFormSet(BaseModelFormSet): - - form = TransitionForm - can_delete = True - extra = 2 - max_num = 0 - can_order = False - model = Transition - - def __init__(self, *args, **kwargs): - self.wg = kwargs.pop('wg', None) - self.user = kwargs.pop('user', None) - super(TransitionFormSet, self).__init__(*args, **kwargs) - - def _construct_form(self, i, **kwargs): - kwargs = kwargs or {} - kwargs.update({'wg': self.wg, 'user': self.user}) - return super(TransitionFormSet, self)._construct_form(i, **kwargs) - - def as_table(self): - html = u'' - csscl = 'oddrow' - for form in self.forms: - html += u'' % csscl - html += form.as_row() - html += u'' - if csscl == 'oddrow': - csscl = 'evenrow' - else: - csscl = 'oddrow' - return mark_safe(u'\n'.join([unicode(self.management_form), html])) - - -def workflow_form_factory(request, wg, user): - - if request.POST.get('update_transitions', None): - return TransitionFormSet(wg=wg, user=user, data=request.POST) - elif request.POST.get('update_states', None): - return StateForm(wg=wg, user=user, data=request.POST) - return TagForm(wg=wg, user=user, data=request.POST) - - -class RemoveDelegateForm(RelatedWGForm): - - delete = forms.MultipleChoiceField() - - def __init__(self, *args, **kwargs): - super(RemoveDelegateForm, self).__init__(*args, **kwargs) - self.fields['delete'].choices = [(i.pk, i.pk) for i in self.wg.wgdelegate_set.all()] - - def save(self): - delegates = self.cleaned_data.get('delete') - save_group_in_history(Group.objects.get(pk=self.wg.pk)) - WGDelegate.objects.filter(pk__in=delegates).delete() - self.set_message('success', 'Delegates removed') - -def assign_shepherd(user, internetdraft, shepherd): - if internetdraft.shepherd == shepherd: - return - - from ietf.doc.models import save_document_in_history, DocEvent, Document - - # saving the proxy object is a bit of a mess, so convert it to a - # proper document - doc = Document.objects.get(name=internetdraft.name) - - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - doc.shepherd = shepherd - doc.save() - - e = DocEvent(type="changed_document") - e.time = doc.time - e.doc = doc - e.by = user.get_profile() - if not shepherd: - e.desc = u"Unassigned shepherd" - else: - e.desc = u"Changed shepherd to %s" % shepherd.plain_name() - e.save() - - # update proxy too - internetdraft.shepherd = shepherd - -class AddDelegateForm(RelatedWGForm): - - email = forms.EmailField() - form_type = forms.CharField(widget=forms.HiddenInput, initial='single') - - def __init__(self, *args, **kwargs): - self.shepherd = kwargs.pop('shepherd', False) - super(AddDelegateForm, self).__init__(*args, **kwargs) - self.next_form = self - - def get_next_form(self): - return self.next_form - - def get_person(self, email): - persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).filter( - Q(iesglogin__isnull=False)| - Q(legacywgpassword__isnull=False)| - Q(legacyliaisonuser__isnull=False)).distinct() - if not persons: - raise PersonOrOrgInfo.DoesNotExist - if len(persons) > 1: - raise PersonOrOrgInfo.MultipleObjectsReturned - return persons[0] - - def save(self): - email = self.cleaned_data.get('email') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - try: - person = Person.objects.filter(email__address=email).exclude(user=None).distinct().get() - except Person.DoesNotExist: - self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system') - return - except Person.MultipleObjectsReturned: - self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('multiple', 'There are multiple users with this email in the system') - return - else: - try: - person = self.get_person(email) - except PersonOrOrgInfo.DoesNotExist: - self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system') - return - except PersonOrOrgInfo.MultipleObjectsReturned: - self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('multiple', 'There are multiple users with this email in the system') - return - if self.shepherd: - self.assign_shepherd(person) - else: - self.create_delegate(person) - - def assign_shepherd(self, person): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - assign_shepherd(self.user, self.shepherd, person) - else: - self.shepherd.shepherd = person - self.shepherd.save() - self.next_form = AddDelegateForm(wg=self.wg, user=self.user, shepherd=self.shepherd) - self.next_form.set_message('success', 'Shepherd assigned successfully') - - def create_delegate(self, person): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - created = False - e = Email.objects.get(address=self.cleaned_data.get('email')) - if not Role.objects.filter(name="delegate", group=self.wg, person=person, email=e): - created = True - save_group_in_history(Group.objects.get(pk=self.wg.pk)) - delegate, _ = Role.objects.get_or_create( - name=RoleName.objects.get(slug="delegate"), group=self.wg, person=e.person, email=e) - else: - (delegate, created) = WGDelegate.objects.get_or_create(wg=self.wg, - person=person) - if not created: - self.set_message('error', 'The email belongs to a person who is already a delegate') - else: - self.next_form = AddDelegateForm(wg=self.wg, user=self.user) - self.next_form.set_message('success', 'A new delegate has been added') - - -class MultipleDelegateForm(AddDelegateForm): - - email = forms.EmailField(widget=forms.HiddenInput) - form_type = forms.CharField(widget=forms.HiddenInput, initial='multiple') - persons = forms.ChoiceField(widget=forms.RadioSelect, help_text='Please select one person from the list') - submit_msg = 'Designate as delegate' - - def __init__(self, *args, **kwargs): - self.email = kwargs.pop('email', None) - super(MultipleDelegateForm, self).__init__(*args, **kwargs) - if not self.email: - self.email = self.data.get('email', None) - self.fields['email'].initial = self.email - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - self.fields['persons'].choices = [(i.pk, unicode(i)) for i in Person.objects.filter(email__address=self.email).exclude(user=None).distinct().order_by('name')] - else: - self.fields['persons'].choices = [(i.pk, unicode(i)) for i in PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).filter( - Q(iesglogin__isnull=False)| - Q(legacywgpassword__isnull=False)| - Q(legacyliaisonuser__isnull=False)).distinct().order_by('first_name')] - - def save(self): - person_id = self.cleaned_data.get('persons') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - person = Person.objects.get(pk=person_id) - else: - person = PersonOrOrgInfo.objects.get(pk=person_id) - if self.shepherd: - self.assign_shepherd(person) - else: - self.create_delegate(person) - - -class NotExistDelegateForm(MultipleDelegateForm): - - email = forms.EmailField(widget=forms.HiddenInput) - form_type = forms.CharField(widget=forms.HiddenInput, initial='notexist') - can_cancel = True - submit_msg = 'Send email to these addresses' - - def __init__(self, *args, **kwargs): - super(NotExistDelegateForm, self).__init__(*args, **kwargs) - self.email_list = [] - del(self.fields['persons']) - - def get_email_list(self): - if self.email_list: - return self.email_list - email_list = [self.email] - email_list.append('IETF Secretariat ') - email_list += ['%s <%s>' % i.person.email() for i in self.wg.wgchair_set.all() if i.person.email()] - self.email_list = email_list - return email_list - - def as_p(self): - email_list = self.get_email_list() - info = render_to_string('wgchairs/notexistdelegate.html', {'email_list': email_list, 'shepherd': self.shepherd}) - return info + super(NotExistDelegateForm, self).as_p() - - def send_email(self, to_email, template): - if self.shepherd: - subject = 'WG shepherd needs system credentials' - else: - subject = 'WG Delegate needs system credentials' - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - persons = Person.objects.filter(email__address=self.email).distinct() - else: - persons = PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).distinct() - body = render_to_string(template, - {'chair': get_person_for_user(self.user), - 'delegate_email': self.email, - 'shepherd': self.shepherd, - 'delegate_persons': persons, - 'wg': self.wg, - }) - - send_mail_text(self.request, to_email, settings.DEFAULT_FROM_EMAIL, subject, body) - - def save(self): - self.next_form = AddDelegateForm(wg=self.wg, user=self.user) - if settings.DEBUG: - self.next_form.set_message('warning', 'Email was not sent cause tool is in DEBUG mode') - else: - # this is ugly... - email_list = self.get_email_list() - delegate = email_list[0] - secretariat = email_list[1] - wgchairs = email_list[2:] - self.send_email(delegate, 'wgchairs/notexistsdelegate_delegate_email.txt') - self.send_email(secretariat, 'wgchairs/notexistsdelegate_secretariat_email.txt') - self.send_email(wgchairs, 'wgchairs/notexistsdelegate_wgchairs_email.txt') - self.next_form.set_message('success', 'Email sent successfully') - - -def add_form_factory(request, wg, user, shepherd=False): - if request.method != 'POST' or request.POST.get('update_shepehrd'): - return AddDelegateForm(wg=wg, user=user, shepherd=shepherd) - - if request.POST.get('form_type', None) == 'multiple': - f = MultipleDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd) - elif request.POST.get('form_type', None) == 'notexist': - f = NotExistDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd) - elif request.POST.get('form_type', None) == 'single': - f = AddDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd) - else: - f = AddDelegateForm(wg=wg, user=user, shepherd=shepherd) - - f.request = request - return f - -class WriteUpEditForm(RelatedWGForm): - - writeup = forms.CharField(widget=forms.Textarea, required=False) - followup = forms.BooleanField(required=False) - comment = forms.CharField(widget=forms.Textarea, required=False) - - def __init__(self, *args, **kwargs): - self.doc = kwargs.pop('doc', None) - self.doc_writeup = self.doc.protowriteup_set.all() - if self.doc_writeup.count(): - self.doc_writeup = self.doc_writeup[0] - else: - self.doc_writeup = None - super(WriteUpEditForm, self).__init__(*args, **kwargs) - self.person = get_person_for_user(self.user) - - def get_writeup(self): - return self.data.get('writeup', self.doc_writeup and self.doc_writeup.writeup or '') - - def save(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - e = WriteupDocEvent(type="changed_protocol_writeup") - e.doc = self.doc - e.by = self.person - e.desc = e.get_type_display() - e.text = self.cleaned_data['writeup'] - e.save() - from ietf.wgchairs.models import ProtoWriteUpProxy - self.doc_writeup = ProtoWriteUpProxy.objects.get(pk=e.pk) - else: - if not self.doc_writeup: - self.doc_writeup = ProtoWriteUp.objects.create( - person=self.person, - draft=self.doc, - writeup=self.cleaned_data['writeup']) - else: - self.doc_writeup.writeup = self.cleaned_data['writeup'] - self.doc_writeup.save() - - if self.data.get('modify_tag', False): - followup = self.cleaned_data.get('followup', False) - comment = self.cleaned_data.get('comment', False) - try: - shepherd = self.doc.shepherd - except PersonOrOrgInfo.DoesNotExist: - shepherd = None - if shepherd: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - extra_notify = [shepherd.formatted_email()] - else: - extra_notify = ['%s <%s>' % shepherd.email()] - else: - extra_notify = [] - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - tags = DocTagName.objects.filter(slug="sheph-u") - else: - tags = [FOLLOWUP_TAG] - if followup: - update_tags(self.request, self.doc, comment, self.person, set_tags=tags, extra_notify=extra_notify) - else: - update_tags(self.request, self.doc, comment, self.person, reset_tags=tags, extra_notify=extra_notify) - return self.doc_writeup - - def is_valid(self): - if self.data.get('confirm', False) and self.data.get('modify_tag', False): - self.fields['comment'].required = True - else: - self.fields['comment'].required = False - return super(WriteUpEditForm, self).is_valid() diff --git a/ietf/wgchairs/migrations/.gitignore b/ietf/wgchairs/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/wgchairs/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/wgchairs/migrations/0001_initial.py b/ietf/wgchairs/migrations/0001_initial.py deleted file mode 100644 index 6aa9fb58f..000000000 --- a/ietf/wgchairs/migrations/0001_initial.py +++ /dev/null @@ -1,110 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.wgchairs.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'WGDelegate' - db.create_table('wgchairs_wgdelegate', ( - ('id', orm['wgchairs.WGDelegate:id']), - ('person', orm['wgchairs.WGDelegate:person']), - ('wg', orm['wgchairs.WGDelegate:wg']), - )) - db.send_create_signal('wgchairs', ['WGDelegate']) - - - - def backwards(self, orm): - - # Deleting model 'WGDelegate' - db.delete_table('wgchairs_wgdelegate') - - - - models = { - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.area': { - 'Meta': {'db_table': "'areas'"}, - 'area_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'extra_email_addresses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaStatus']"}) - }, - 'idtracker.areadirector': { - 'Meta': {'db_table': "'area_directors'"}, - 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Area']", 'null': 'True', 'db_column': "'area_acronym_id'"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"}) - }, - 'idtracker.areastatus': { - 'Meta': {'db_table': "'area_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.ietfwg': { - 'Meta': {'db_table': "'groups_ietf'"}, - 'area_director': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaDirector']", 'null': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'dormant_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), - 'email_archive': ('django.db.models.fields.CharField', [], {'max_length': '95', 'blank': 'True'}), - 'email_keyword': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'email_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'}), - 'group_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'group_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGType']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'meeting_scheduled': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'meeting_scheduled_old': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'proposed_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGStatus']"}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'idtracker.wgstatus': { - 'Meta': {'db_table': "'g_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.wgtype': { - 'Meta': {'db_table': "'g_type'"}, - 'group_type_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'type': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'group_type'"}) - }, - 'wgchairs.wgdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'wg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IETFWG']"}) - } - } - - complete_apps = ['wgchairs'] diff --git a/ietf/wgchairs/migrations/0002_add_writeup.py b/ietf/wgchairs/migrations/0002_add_writeup.py deleted file mode 100644 index e1754ed22..000000000 --- a/ietf/wgchairs/migrations/0002_add_writeup.py +++ /dev/null @@ -1,163 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.wgchairs.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'ProtoWriteUp' - db.create_table('wgchairs_protowriteup', ( - ('id', orm['wgchairs.protowriteup:id']), - ('person', orm['wgchairs.protowriteup:person']), - ('draft', orm['wgchairs.protowriteup:draft']), - ('date', orm['wgchairs.protowriteup:date']), - ('writeup', orm['wgchairs.protowriteup:writeup']), - )) - db.send_create_signal('wgchairs', ['ProtoWriteUp']) - - - - def backwards(self, orm): - - # Deleting model 'ProtoWriteUp' - db.delete_table('wgchairs_protowriteup') - - - - models = { - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.area': { - 'Meta': {'db_table': "'areas'"}, - 'area_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'extra_email_addresses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaStatus']"}) - }, - 'idtracker.areadirector': { - 'Meta': {'db_table': "'area_directors'"}, - 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Area']", 'null': 'True', 'db_column': "'area_acronym_id'"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"}) - }, - 'idtracker.areastatus': { - 'Meta': {'db_table': "'area_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.ietfwg': { - 'Meta': {'db_table': "'groups_ietf'"}, - 'area_director': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaDirector']", 'null': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'dormant_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), - 'email_archive': ('django.db.models.fields.CharField', [], {'max_length': '95', 'blank': 'True'}), - 'email_keyword': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'email_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'}), - 'group_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'group_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGType']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'meeting_scheduled': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'meeting_scheduled_old': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'proposed_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGStatus']"}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'idtracker.wgstatus': { - 'Meta': {'db_table': "'g_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.wgtype': { - 'Meta': {'db_table': "'g_type'"}, - 'group_type_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'type': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'group_type'"}) - }, - 'wgchairs.protowriteup': { - 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now()'}), - 'draft': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.InternetDraft']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'writeup': ('django.db.models.fields.TextField', [], {}) - }, - 'wgchairs.wgdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'wg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IETFWG']"}) - } - } - - complete_apps = ['wgchairs'] diff --git a/ietf/wgchairs/migrations/__init__.py b/ietf/wgchairs/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/wgchairs/models.py b/ietf/wgchairs/models.py deleted file mode 100644 index ea0a74c97..000000000 --- a/ietf/wgchairs/models.py +++ /dev/null @@ -1,87 +0,0 @@ -import datetime - -from django.db import models -from django.conf import settings - -from ietf.idtracker.models import (IETFWG, PersonOrOrgInfo, - InternetDraft) - - -class WGDelegate(models.Model): - person = models.ForeignKey( - PersonOrOrgInfo, - ) - - wg = models.ForeignKey(IETFWG, related_name="old_wgdelegate_set" if settings.USE_DB_REDESIGN_PROXY_CLASSES else None) - - def __unicode__(self): - return "%s" % self.person - - class Meta: - verbose_name = "WG Delegate" - -class ProtoWriteUp(models.Model): - person = models.ForeignKey( - PersonOrOrgInfo, - blank=False, - null=False, - ) - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.idtracker.models import InternetDraftOld as InternetDraft - - draft = models.ForeignKey( - InternetDraft, - blank=False, - null=False, - ) - - date = models.DateTimeField( - default=datetime.datetime.now(), - blank=False, - null=False, - ) - - writeup = models.TextField( - blank=False, - null=False, - ) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.group.models import Role - class WGDelegateProxy(Role): - #person = models.ForeignKey(PersonOrOrgInfo) # same name - #wg = models.ForeignKey(IETFWG) - @property - def wg(self): - return self.group - - def __unicode__(self): - return u"%s" % self.person - - class Meta: - proxy = True - - from ietf.doc.models import WriteupDocEvent - class ProtoWriteUpProxy(WriteupDocEvent): - #person = models.ForeignKey(PersonOrOrgInfo, blank=False, null=False) - @property - def person(self): - return self.by - #draft = models.ForeignKey(InternetDraft, blank=False, null=False) - @property - def draft(self): - return self.doc - #date = models.DateTimeField(default=datetime.datetime.now(), blank=False, null=False) - @property - def date(self): - return self.time - #writeup = models.TextField(blank=False, null=False) - @property - def writeup(self): - return self.text - class Meta: - proxy = True - - #WGDelegateOld = WGDelegate - WGDelegate = WGDelegateProxy diff --git a/ietf/wgchairs/templatetags/.gitignore b/ietf/wgchairs/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/wgchairs/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/wgchairs/templatetags/__init__.py b/ietf/wgchairs/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/wgchairs/templatetags/wgchairs_tags.py b/ietf/wgchairs/templatetags/wgchairs_tags.py deleted file mode 100644 index df51dafae..000000000 --- a/ietf/wgchairs/templatetags/wgchairs_tags.py +++ /dev/null @@ -1,47 +0,0 @@ -from django.conf import settings -from django import template - -from ietf.ietfworkflows.utils import get_state_for_draft -from ietf.wgchairs.accounts import (can_manage_workflow_in_group, - can_manage_delegates_in_group, - can_manage_shepherds_in_group) - - -register = template.Library() - - -@register.inclusion_tag('wgchairs/wgchairs_admin_options.html', takes_context=True) -def wgchairs_admin_options(context, wg): - request = context.get('request', None) - user = request and request.user - return {'user': user, - 'can_manage_delegates': can_manage_delegates_in_group(user, wg), - 'can_manage_workflow': can_manage_workflow_in_group(user, wg), - 'can_manage_shepherds': can_manage_shepherds_in_group(user, wg), - 'wg': wg, - 'selected': context.get('selected', None), - } - -@register.simple_tag -def writeup(doc): - writeup = doc.protowriteup_set.all() - if not writeup: - return '' - else: - return writeup[0].writeup - - -@register.simple_tag -def writeupdate(doc): - writeup = doc.protowriteup_set.all() - if not writeup: - return '' - else: - return writeup[0].date - - -@register.inclusion_tag('wgchairs/draft_state.html', takes_context=True) -def show_state(context, doc): - return {'doc': doc, - 'state': get_state_for_draft(doc), - } diff --git a/ietf/wgchairs/tests.py b/ietf/wgchairs/tests.py deleted file mode 100644 index 8433e9399..000000000 --- a/ietf/wgchairs/tests.py +++ /dev/null @@ -1,214 +0,0 @@ -import datetime, os, shutil - -from django.conf import settings -from django.contrib.auth.models import User -from django.core.urlresolvers import reverse as urlreverse -import django.test -from StringIO import StringIO -from pyquery import PyQuery - -from ietf.utils.test_utils import login_testing_unauthorized -from ietf.utils.test_data import make_test_data -from ietf.utils.mail import outbox - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.person.models import Person, Email - from ietf.group.models import Group, GroupHistory, Role, GroupStateTransitions - from ietf.doc.models import Document, State, WriteupDocEvent - from ietf.name.models import DocTagName - -class ManageDelegatesTestCase(django.test.TestCase): - fixtures = ['names'] - - def test_delete_delegate(self): - make_test_data() - - url = urlreverse('manage_delegates', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - delegates = Role.objects.filter(name="delegate", group__acronym="mars") - self.assertTrue(len(delegates) > 0) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=delete]')), len(delegates)) - - # delete - r = self.client.post(url, - dict(remove="1", - delete=[d.pk for d in delegates])) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=delete]')), 0) - self.assertEquals(Role.objects.filter(name="delegate", group__acronym="mars").count(), 0) - - def test_add_not_existing_delegate(self): - make_test_data() - - url = urlreverse('manage_delegates', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=email]')), 1) - - # add non-existing - r = self.client.post(url, - dict(email="unknown@example.com", - form_type="single")) - self.assertEquals(r.status_code, 200) - self.assertTrue("unknown@example.com" in r.content) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[type=submit][value*="Send email"]')), 1) - - # we get back a warning and offer to send email, do that - mailbox_before = len(outbox) - r = self.client.post(url, - dict(email="unknown@example.com", - form_type="notexist")) - self.assertEquals(r.status_code, 200) - self.assertTrue("Email sent" in r.content) - self.assertEquals(len(outbox), mailbox_before + 3) - - def test_add_delegate(self): - make_test_data() - - url = urlreverse('manage_delegates', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=email]')), 1) - - # add existing person - history_before = GroupHistory.objects.filter(acronym="mars").count() - r = self.client.post(url, - dict(email="plain@example.com", - form_type="single")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue("new delegate" in r.content) - self.assertTrue(Email.objects.get(address="plain@example.com").person.plain_name() in r.content) - self.assertEquals(Role.objects.filter(name="delegate", group__acronym="mars", email__address="plain@example.com").count(), 1) - self.assertEquals(history_before + 1, GroupHistory.objects.filter(acronym="mars").count()) - - -class ManageShepherdsTestCase(django.test.TestCase): - fixtures = ['names'] - - def test_manage_shepherds(self): - make_test_data() - - url = urlreverse('manage_shepherds', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - # setup test documents - group = Group.objects.get(acronym="mars") - - from ietf.doc.models import Document - common = dict(group=group, - ad=Person.objects.get(user__username="ad"), - type_id="draft") - Document.objects.create(name="test-shepherd-no", - title="No shepherd", - shepherd=None, - **common) - Document.objects.create(name="test-shepherd-me", - title="Shepherd me", - shepherd=Person.objects.get(user__username="secretary"), - **common) - Document.objects.create(name="test-shepherd-other", title="Shepherd other", - shepherd=Person.objects.get(user__username="plain"), - **common) - for d in Document.objects.filter(name__startswith="test-shepherd"): - d.set_state(State.objects.get(used=True, type="draft", slug="active")) - - # get and make sure they are divided correctly - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('div#noshepherd a:contains("No shepherd")')), 1) - self.assertEquals(len(q('div#mydocs a:contains("Shepherd me")')), 1) - self.assertEquals(len(q('div#othershepherds a:contains("Shepherd other")')), 1) - - -class ManageWorkflowTestCase(django.test.TestCase): - fixtures = ['names'] - - def test_manage_workflows(self): - make_test_data() - - group = Group.objects.get(acronym="mars") - - url = urlreverse('manage_workflow', kwargs=dict(acronym=group.acronym)) - login_testing_unauthorized(self, "secretary", url) - - state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-lc") - self.assertTrue(state not in group.unused_states.all()) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='0']")), 1) - - # deactivate state - r = self.client.post(url, - dict(action="setstateactive", - state=state.pk, - active="0")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='1']")), 1) - group = Group.objects.get(acronym=group.acronym) - self.assertTrue(state in group.unused_states.all()) - - # change next states - state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-doc") - next_states = State.objects.filter(used=True, type=b"draft-stream-ietf", slug__in=["parked", "dead", "wait-wgw", 'sub-pub']).values_list('pk', flat=True) - r = self.client.post(url, - dict(action="setnextstates", - state=state.pk, - next_states=next_states)) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q("form.set-next-states").find("input[name=state][value='%s']" % state.pk).parents('form').find("input[name=next_states][checked=checked]")), len(next_states)) - transitions = GroupStateTransitions.objects.filter(group=group, state=state) - self.assertEquals(len(transitions), 1) - self.assertEquals(set(transitions[0].next_states.values_list("pk", flat=True)), set(next_states)) - - # change them back to default - next_states = state.next_states.values_list("pk", flat=True) - r = self.client.post(url, - dict(action="setnextstates", - state=state.pk, - next_states=next_states)) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - transitions = GroupStateTransitions.objects.filter(group=group, state=state) - self.assertEquals(len(transitions), 0) - - # deactivate tag - tag = DocTagName.objects.get(slug="w-expert") - r = self.client.post(url, - dict(action="settagactive", - tag=tag.pk, - active="0")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form').find('input[name=tag][value="%s"]' % tag.pk).parents("form").find("input[name=active]")), 1) - group = Group.objects.get(acronym=group.acronym) - self.assertTrue(tag in group.unused_tags.all()) - -if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - # the above tests only work with the new schema - del ManageDelegatesTestCase - del ManageShepherdsTestCase - del ManageWorkflowTestCase - del ManageWriteupCase diff --git a/ietf/wgchairs/urls.py b/ietf/wgchairs/urls.py deleted file mode 100644 index b9b32c240..000000000 --- a/ietf/wgchairs/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright The IETF Trust 2008, All Rights Reserved - -from django.conf.urls.defaults import patterns, url - -urlpatterns = patterns('ietf.wgchairs.views', - url(r'^delegates/$', 'manage_delegates', name='manage_delegates'), -) diff --git a/ietf/wgchairs/views.py b/ietf/wgchairs/views.py deleted file mode 100644 index 1f9552e3d..000000000 --- a/ietf/wgchairs/views.py +++ /dev/null @@ -1,73 +0,0 @@ -from django.conf import settings -from ietf.idtracker.models import IETFWG, InternetDraft -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext -from django.http import HttpResponseForbidden, Http404 - -from ietf.wgchairs.forms import (RemoveDelegateForm, add_form_factory, - workflow_form_factory, TransitionFormSet, - WriteUpEditForm, assign_shepherd) -from ietf.wgchairs.accounts import (can_manage_delegates_in_group, get_person_for_user, - can_manage_shepherds_in_group, - can_manage_workflow_in_group, - can_manage_shepherd_of_a_document, - can_manage_writeup_of_a_document, - can_manage_writeup_of_a_document_no_state, - ) -from ietf.ietfworkflows.constants import REQUIRED_STATES -from ietf.ietfworkflows.utils import (get_workflow_for_wg, - get_default_workflow_for_wg, - get_state_by_name, - get_annotation_tags_for_draft, - get_state_for_draft, WAITING_WRITEUP, - FOLLOWUP_TAG) -from ietf.name.models import DocTagName -from ietf.doc.models import State -from ietf.doc.utils import get_tags_for_stream_id - -def manage_delegates(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_delegates_in_group(user, wg): - return HttpResponseForbidden('You have no permission to access this view') - delegates = wg.wgdelegate_set.all() - add_form = add_form_factory(request, wg, user) - if request.method == 'POST': - if request.POST.get('remove', None): - form = RemoveDelegateForm(wg=wg, data=request.POST.copy()) - if form.is_valid(): - form.save() - elif add_form.is_valid(): - add_form.save() - add_form = add_form.get_next_form() - max_delegates = getattr(settings, 'MAX_WG_DELEGATES', 3) - return render_to_response('wgchairs/manage_delegates.html', - {'wg': wg, - 'delegates': delegates, - 'selected': 'manage_delegates', - 'can_add': delegates.count() < max_delegates, - 'max_delegates': max_delegates, - 'add_form': add_form, - }, RequestContext(request)) - - -def wg_shepherd_documents(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_shepherds_in_group(user, wg): - return HttpResponseForbidden('You have no permission to access this view') - current_person = get_person_for_user(user) - - base_qs = InternetDraft.objects.filter(group=wg, states__type="draft", states__slug="active").select_related("status").order_by('title') - documents_no_shepherd = base_qs.filter(shepherd=None) - documents_my = base_qs.filter(shepherd=current_person) - documents_other = base_qs.exclude(shepherd=None).exclude(shepherd__pk__in=[current_person.pk, 0]) - context = { - 'no_shepherd': documents_no_shepherd, - 'my_documents': documents_my, - 'other_shepherds': documents_other, - 'selected': 'manage_shepherds', - 'wg': wg, - } - return render_to_response('wgchairs/wg_shepherd_documents.html', context, RequestContext(request)) - From e1bd47b509f92652cff2a748479a4228404382ce Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 20 Sep 2013 11:02:06 +0000 Subject: [PATCH 021/173] Add ietf. qualifier to settings import, this fixes a weird bug where model files apparently get the wrong settings - Legacy-Id: 6216 --- ietf/settings_sqlitetest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ietf/settings_sqlitetest.py b/ietf/settings_sqlitetest.py index 80e497563..9190948c7 100644 --- a/ietf/settings_sqlitetest.py +++ b/ietf/settings_sqlitetest.py @@ -4,7 +4,8 @@ # ./manage.py test --settings=settings_sqlitetest doc.ChangeStateTestCase # -from settings import * +from ietf.settings import * + DATABASES = { 'default': { 'NAME': 'test.db', From 0eed4ad9e5671da34a798935dd278acb5ad6a24f Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 20 Sep 2013 11:23:17 +0000 Subject: [PATCH 022/173] Add has_role(role_name, user) utility to group, used like group.has_role("chair", request.user). Remove the NomCom specific group utilities, apparently they're not actually used. - Legacy-Id: 6217 --- ietf/group/models.py | 17 ++--------------- ietf/nomcom/models.py | 2 +- ietf/nomcom/templatetags/nomcom_tags.py | 2 +- ietf/nomcom/utils.py | 12 ------------ ietf/nomcom/views.py | 4 ++-- 5 files changed, 6 insertions(+), 31 deletions(-) diff --git a/ietf/group/models.py b/ietf/group/models.py index 68968f16e..8c8ea13a9 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -54,26 +54,13 @@ class Group(GroupInfo): e = model.objects.filter(group=self).filter(**filter_args).order_by('-time', '-id')[:1] return e[0] if e else None - def is_chair(self, user): - chair = self.get_chair() - if chair: - return self.get_chair().person.user == user - else: - return False - - def is_member(self, user): - members = self.get_members() - users = [member.person.user for member in members] - return user in users + def has_role(self, role_name, user): + return user.is_authenticated() and self.role_set.filter(name=role_name, person__user=user).exists() def get_chair(self): chair = self.role_set.filter(name__slug='chair')[:1] return chair and chair[0] or None - def get_members(self): - members = self.role_set.filter(name__slug__in=["chair", "member", "advisor", "liaison"]) - return members - class GroupHistory(GroupInfo): group = models.ForeignKey(Group, related_name='history_set') acronym = models.CharField(max_length=40) diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py index a36f77a95..b58c85b17 100644 --- a/ietf/nomcom/models.py +++ b/ietf/nomcom/models.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import os from django.db import models diff --git a/ietf/nomcom/templatetags/nomcom_tags.py b/ietf/nomcom/templatetags/nomcom_tags.py index f9f98feaa..49b631378 100644 --- a/ietf/nomcom/templatetags/nomcom_tags.py +++ b/ietf/nomcom/templatetags/nomcom_tags.py @@ -24,7 +24,7 @@ def is_chair(user, year): nomcom = get_nomcom_by_year(year=year) if has_role(user, "Secretariat"): return True - return nomcom.group.is_chair(user) + return nomcom.group.has_role("chair", user) @register.filter diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py index 869925b27..8b1a3a2ea 100644 --- a/ietf/nomcom/utils.py +++ b/ietf/nomcom/utils.py @@ -64,18 +64,6 @@ def get_user_email(user): return mail -def is_nomcom_member(user, nomcom): - is_group_member = nomcom.group.is_member(user) - if not is_group_member: - raise PermissionDenied("Must be nomcom member") - - -def is_nomcom_chair(user, nomcom): - is_group_chair = nomcom.group.is_chair(user) - if not is_group_chair: - raise PermissionDenied("Must be nomcom chair") - - def get_hash_nominee_position(date, nominee_position_id): return hashlib.md5('%s%s%s' % (settings.SECRET_KEY, date, nominee_position_id)).hexdigest() diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index 47b0a1776..f18bb5f45 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -1,4 +1,4 @@ - # -*- coding: utf-8 -*- + # -*- coding: utf-8 -*- import datetime @@ -81,7 +81,7 @@ def private_key(request, year): def private_index(request, year): nomcom = get_nomcom_by_year(year) all_nominee_positions = NomineePosition.objects.get_by_nomcom(nomcom).not_duplicated() - is_chair = nomcom.group.is_chair(request.user) + is_chair = nomcom.group.has_role("chair", request.user) message = None if is_chair and request.method == 'POST': action = request.POST.get('action') From 59240245135f3de330ff8ee7970f0d4d070c93d6 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 20 Sep 2013 13:46:47 +0000 Subject: [PATCH 023/173] Swap the arguments to group.has_role so they match the arguments to the existing has_role - Legacy-Id: 6218 --- ietf/group/models.py | 6 ++++-- ietf/nomcom/templatetags/nomcom_tags.py | 2 +- ietf/nomcom/views.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ietf/group/models.py b/ietf/group/models.py index 8c8ea13a9..780c1a47c 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -54,8 +54,10 @@ class Group(GroupInfo): e = model.objects.filter(group=self).filter(**filter_args).order_by('-time', '-id')[:1] return e[0] if e else None - def has_role(self, role_name, user): - return user.is_authenticated() and self.role_set.filter(name=role_name, person__user=user).exists() + def has_role(self, user, role_names): + if isinstance(role_names, str) or isinstance(role_names, unicode): + role_names = [role_names] + return user.is_authenticated() and self.role_set.filter(name__in=role_names, person__user=user).exists() def get_chair(self): chair = self.role_set.filter(name__slug='chair')[:1] diff --git a/ietf/nomcom/templatetags/nomcom_tags.py b/ietf/nomcom/templatetags/nomcom_tags.py index 49b631378..d820c9fae 100644 --- a/ietf/nomcom/templatetags/nomcom_tags.py +++ b/ietf/nomcom/templatetags/nomcom_tags.py @@ -24,7 +24,7 @@ def is_chair(user, year): nomcom = get_nomcom_by_year(year=year) if has_role(user, "Secretariat"): return True - return nomcom.group.has_role("chair", user) + return nomcom.group.has_role(user, "chair") @register.filter diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index f18bb5f45..cccd45441 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -81,7 +81,7 @@ def private_key(request, year): def private_index(request, year): nomcom = get_nomcom_by_year(year) all_nominee_positions = NomineePosition.objects.get_by_nomcom(nomcom).not_duplicated() - is_chair = nomcom.group.has_role("chair", request.user) + is_chair = nomcom.group.has_role(request.user, "chair") message = None if is_chair and request.method == 'POST': action = request.POST.get('action') From 2e29298d0cf3f3292ea609f94f9e47e5071b5d65 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 20 Sep 2013 14:09:30 +0000 Subject: [PATCH 024/173] Move group charter actions from box and button next to header up to an edit menu under the ordinary group menu - Legacy-Id: 6219 --- ietf/templates/wginfo/group_base.html | 10 ++++++++ ietf/templates/wginfo/group_charter.html | 16 +----------- ietf/wginfo/views.py | 32 +++++++++++++----------- static/css/base2.css | 1 + 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/ietf/templates/wginfo/group_base.html b/ietf/templates/wginfo/group_base.html index 1496a7e57..6e1f7907e 100644 --- a/ietf/templates/wginfo/group_base.html +++ b/ietf/templates/wginfo/group_base.html @@ -70,6 +70,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
Documents | Charter | @@ -78,6 +79,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. List Archive » | {% endif %} Tools WG Page » +
+ + {% if menu_actions %} +
+ {% for name, url in menu_actions %} + {{ name }} + {% endfor %} +
+ {% endif %}
{% block group_content %} diff --git a/ietf/templates/wginfo/group_charter.html b/ietf/templates/wginfo/group_charter.html index b29f6a918..8148bc035 100644 --- a/ietf/templates/wginfo/group_charter.html +++ b/ietf/templates/wginfo/group_charter.html @@ -138,13 +138,6 @@ is occasionally incorrect. -{% if user|has_role:"Area Director,Secretariat" %} -
- {% for name, url in actions %} - {{ name }} {% if not forloop.last %} | {% endif %} - {% endfor %} -
-{% endif %}
{% with group.groupurl_set.all as urls %} @@ -161,14 +154,7 @@ is occasionally incorrect.

{{ group.charter_text|escape|format_charter|safe }}

-

{% if group.state_id == "proposed" %}Proposed{% endif %} Milestones - -{% if group.state_id != "proposed" %} -{% if user|has_role:"Area Director,Secretariat" or is_chair %} -Add or edit milestones -{% endif %} -{% endif %} -

+

{% if group.state_id == "proposed" %}Proposed{% endif %} Milestones

{% with group.milestones as milestones %} {% include "wginfo/milestones.html" %} diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 57bc5100c..2a92ad379 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -173,9 +173,27 @@ def chartering_wgs(request): def construct_group_menu_context(request, group, selected, others): """Return context with info for the group menu filled in.""" + actions = [] + + is_chair = group.has_role(request.user, "chair") + is_ad_or_secretariat = has_role(request.user, ("Area Director", "Secretariat")) + + if group.state_id != "proposed" and (is_chair or is_ad_or_secretariat): + actions.append((u"Add or edit milestones", urlreverse("wg_edit_milestones", kwargs=dict(acronym=group.acronym)))) + + if group.state_id != "conclude" and is_ad_or_secretariat: + actions.append((u"Edit group", urlreverse("group_edit", kwargs=dict(acronym=group.acronym)))) + + if is_chair or is_ad_or_secretariat: + actions.append((u"Customize workflow", urlreverse("ietf.wginfo.edit.customize_workflow", kwargs=dict(acronym=group.acronym)))) + + if group.state_id in ("active", "dormant") and is_ad_or_secretariat: + actions.append((u"Request closing group", urlreverse("wg_conclude", kwargs=dict(acronym=group.acronym)))) + d = { "group": group, "selected": selected, + "menu_actions": actions, } d.update(others) @@ -259,25 +277,11 @@ def group_charter(request, acronym): fill_in_charter_info(group, include_drafts=False) group.delegates = Email.objects.filter(role__group=group, role__name="delegate") - actions = [] - if group.state_id != "conclude": - actions.append((u"Edit %s" % group.type.name, urlreverse("group_edit", kwargs=dict(acronym=group.acronym)))) - e = group.latest_event(type__in=("changed_state", "requested_close",)) requested_close = group.state_id != "conclude" and e and e.type == "requested_close" - if group.state_id in ("active", "dormant"): - actions.append((u"Request closing %s" % group.type.name, urlreverse("wg_conclude", kwargs=dict(acronym=group.acronym)))) - - is_chair = request.user.is_authenticated() and group.role_set.filter(name="chair", person__user=request.user) - - if is_chair or has_role(request.user, "Secretariat"): - actions.append((u"Customize workflow", urlreverse("ietf.wginfo.edit.customize_workflow", kwargs=dict(acronym=group.acronym)))) - return render_to_response('wginfo/group_charter.html', construct_group_menu_context(request, group, "charter", { - "actions": actions, - "is_chair": is_chair, "milestones_in_review": group.groupmilestone_set.filter(state="review"), "requested_close": requested_close, }), RequestContext(request)) diff --git a/static/css/base2.css b/static/css/base2.css index 78f1ee963..a53638b2a 100644 --- a/static/css/base2.css +++ b/static/css/base2.css @@ -76,6 +76,7 @@ a img { border: 0; } .ietf-navset .selected { font-weight:bold; padding: 0 3px; } .ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } +.ietf-navset .actions { margin-top: 0.5em; font-style: italic; font-size: 90%; } .ietf-ballot .left { background: #edf5ff; width:160px; padding-left: 10px; } .ietf-ballot .right { padding-left: 15px; padding-right:15px; width:610px;padding-top:0px;} From b4988b2b45e097aefc1e21d262d0b11058825080 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 20 Sep 2013 15:55:28 +0000 Subject: [PATCH 025/173] Make IESG discusses use doc/search/status_columns.html with a quick hack to break dependency on ietfworkflows, remove now unused idrfc/status_columns.html, remove unused iesg/agenda_documents_row_status.html - Legacy-Id: 6220 --- ietf/iesg/views.py | 4 ++ ietf/templates/idrfc/status_columns.html | 51 ------------------- .../iesg/agenda_documents_row_status.html | 4 -- ietf/templates/iesg/discusses.html | 2 +- 4 files changed, 5 insertions(+), 56 deletions(-) delete mode 100644 ietf/templates/idrfc/status_columns.html delete mode 100644 ietf/templates/iesg/agenda_documents_row_status.html diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index f534312d5..8fb253aa6 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -620,6 +620,10 @@ def discusses(request): doc = IdWrapper(draft=d) if doc.in_ietf_process() and doc.ietf_process.has_active_iesg_ballot(): + # quick hack - to be removed when the proxy code is removed + doc.underlying = doc.underlying_document() + doc.underlying.milestones = d.groupmilestone_set.filter(state="active").order_by("time").select_related("group") + res.append(doc) return direct_to_template(request, 'iesg/discusses.html', {'docs':res}) diff --git a/ietf/templates/idrfc/status_columns.html b/ietf/templates/idrfc/status_columns.html deleted file mode 100644 index 00491018e..000000000 --- a/ietf/templates/idrfc/status_columns.html +++ /dev/null @@ -1,51 +0,0 @@ -{% comment %} -Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% load ietf_filters ietf_streams %}{% load ballot_icon %} - -{{ doc.friendly_state|safe }} {% if not doc.rfc %}{{ doc.id|state_age_colored|safe }}{% endif %} -{% if not hide_telechat_date %}{% if doc.telechat_date %}
IESG Telechat: {{ doc.telechat_date }}{% endif %}{% endif %} - -{% block extra_status %}{% endblock %} -{% if doc.rfc %} -{% if doc.rfc.obsoleted_by %}
Obsoleted by {{ doc.rfc.obsoleted_by|urlize_ietf_docs }}{%endif %} -{% if doc.rfc.updated_by %}
Updated by {{ doc.rfc.updated_by|urlize_ietf_docs }}{%endif %} -{% if doc.rfc.has_errata %}
Errata{% endif %} -{% else %}{# not rfc #} -{% if doc.id.rfc_editor_state %}
RFC Editor State: {{ doc.id.rfc_editor_state|escape }}{% endif %} -{% stream_state doc %} -{% endif %} - - -{% if doc.rfc and doc.rfc.in_ietf_process and doc.rfc.ietf_process.has_active_iesg_ballot %}{% ballot_icon doc.rfc %}{% else %}{% if doc.id %}{% ballot_icon doc.id %}{%endif%}{%endif%} - diff --git a/ietf/templates/iesg/agenda_documents_row_status.html b/ietf/templates/iesg/agenda_documents_row_status.html deleted file mode 100644 index b70099152..000000000 --- a/ietf/templates/iesg/agenda_documents_row_status.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "idrfc/status_columns.html" %} -{% block extra_status %} -
Intended status: {{doc.ietf_process.intended_maturity_level}} -{% endblock %} diff --git a/ietf/templates/iesg/discusses.html b/ietf/templates/iesg/discusses.html index 99e7578fe..6682b617e 100644 --- a/ietf/templates/iesg/discusses.html +++ b/ietf/templates/iesg/discusses.html @@ -58,7 +58,7 @@ Show: {{ doc.displayname_with_link|safe }} -{% include "idrfc/status_columns.html" %} +{% with doc.underlying as doc %}{% include "doc/search/status_columns.html" %}{% endwith %} {{ doc.ad_name|default:"" }} {% for po in doc.ietf_process.iesg_ballot.get_discuss|dictsort:"is_old_ad" %} From 705322f4a1ab4d735ed2ab512da08c49dc43c177 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 23 Sep 2013 10:47:34 +0000 Subject: [PATCH 026/173] Add a select_related to role extraction in wginfo to reduce number of DB queries a bit - Legacy-Id: 6247 --- ietf/wginfo/views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 2a92ad379..97bf5959a 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -53,10 +53,10 @@ from ietf.ietfauth.utils import has_role def fill_in_charter_info(group, include_drafts=False): group.areadirector = group.ad.role_email("ad", group.parent) if group.ad else None - group.chairs = Email.objects.filter(role__group=group, role__name="chair") - group.techadvisors = Email.objects.filter(role__group=group, role__name="techadv") - group.editors = Email.objects.filter(role__group=group, role__name="editor") - group.secretaries = Email.objects.filter(role__group=group, role__name="secr") + group.chairs = Email.objects.filter(role__group=group, role__name="chair").select_related("person") + group.techadvisors = Email.objects.filter(role__group=group, role__name="techadv").select_related("person") + group.editors = Email.objects.filter(role__group=group, role__name="editor").select_related("person") + group.secretaries = Email.objects.filter(role__group=group, role__name="secr").select_related("person") milestone_state = "charter" if group.state_id == "proposed" else "active" group.milestones = group.groupmilestone_set.filter(state=milestone_state).order_by('due') @@ -275,7 +275,7 @@ def group_charter(request, acronym): group = get_object_or_404(Group, type="wg", acronym=acronym) fill_in_charter_info(group, include_drafts=False) - group.delegates = Email.objects.filter(role__group=group, role__name="delegate") + group.delegates = Email.objects.filter(role__group=group, role__name="delegate").select_related("person") e = group.latest_event(type__in=("changed_state", "requested_close",)) requested_close = group.state_id != "conclude" and e and e.type == "requested_close" From 210a90e0b4dd75884f46dde240b7a12a69f84ed8 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 24 Sep 2013 16:53:23 +0000 Subject: [PATCH 027/173] Remove unused ietfworkflows import - Legacy-Id: 6253 --- ietf/community/display.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ietf/community/display.py b/ietf/community/display.py index ac93159d0..3164192cc 100644 --- a/ietf/community/display.py +++ b/ietf/community/display.py @@ -3,7 +3,6 @@ import datetime from django.db.models import Q from django.core.urlresolvers import reverse as urlreverse -from ietf.ietfworkflows.utils import get_state_for_draft from ietf.doc.models import DocAlias, DocEvent From 5f6f966355bb33fa22f784be589bf3ab249ebd0c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 24 Sep 2013 16:55:23 +0000 Subject: [PATCH 028/173] Add stream edit page for setting delegates - Legacy-Id: 6254 --- ietf/group/stream_urls.py | 1 + ietf/group/views.py | 54 ++++++++++++++++++++++++-- ietf/templates/group/stream_edit.html | 55 +++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 ietf/templates/group/stream_edit.html diff --git a/ietf/group/stream_urls.py b/ietf/group/stream_urls.py index c256c41a7..4af4c58a8 100644 --- a/ietf/group/stream_urls.py +++ b/ietf/group/stream_urls.py @@ -7,6 +7,7 @@ import views urlpatterns = patterns('', (r'^$', views.streams), (r'^(?P[a-zA-Z0-9-]+)/$', views.stream_documents, None), + (r'^(?P[a-zA-Z0-9-]+)/edit/$', views.stream_edit), # (r'^(?P[a-zA-Z0-9-]+)/history/$', views.stream_history), # (r'^(?P[a-zA-Z0-9-]+)/edit/$', views.stream_edit) (r'^management/', include('ietf.ietfworkflows.urls')), diff --git a/ietf/group/views.py b/ietf/group/views.py index 9d7eefbbd..8795f2306 100644 --- a/ietf/group/views.py +++ b/ietf/group/views.py @@ -1,13 +1,17 @@ # Copyright The IETF Trust 2008, All Rights Reserved -from django.shortcuts import render_to_response +from django.shortcuts import render_to_response, get_object_or_404, redirect from django.template import RequestContext, loader -from django.http import Http404, HttpResponse +from django.http import Http404, HttpResponse, HttpResponseForbidden +from django import forms -from ietf.group.models import Group +from ietf.group.models import * +from ietf.group.utils import * from ietf.doc.models import Document from ietf.doc.views_search import SearchForm, retrieve_search_results from ietf.name.models import StreamName +from ietf.ietfauth.utils import has_role +from ietf.person.forms import EmailsField import debug @@ -26,4 +30,46 @@ def stream_documents(request, acronym): docs, meta = retrieve_search_results(form) return render_to_response('group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta }, context_instance=RequestContext(request)) - \ No newline at end of file +class StreamEditForm(forms.Form): + delegates = EmailsField(label="Delegates", required=False, help_text=u"Type in name to search for person") + +def stream_edit(request, acronym): + group = get_object_or_404(Group, acronym=acronym) + + if not (has_role(request.user, "Secretariat") or group.has_role(request.user, "chair")): + return HttpResponseForbidden("You don't have permission to access this page.") + + chairs = Email.objects.filter(role__group=group, role__name="chair").select_related("person") + + if request.method == 'POST': + form = StreamEditForm(request.POST) + + if form.is_valid(): + save_group_in_history(group) + + # update roles + attr, slug, title = ('delegates', 'delegate', "Delegates") + + new = form.cleaned_data[attr] + old = Email.objects.filter(role__group=group, role__name=slug).select_related("person") + if set(new) != set(old): + desc = "%s changed to %s from %s" % ( + title, ", ".join(x.get_name() for x in new), ", ".join(x.get_name() for x in old)) + + GroupEvent.objects.create(group=group, by=request.user.get_profile(), type="info_changed", desc=desc) + + group.role_set.filter(name=slug).delete() + for e in new: + Role.objects.get_or_create(name_id=slug, email=e, group=group, person=e.person) + + return redirect("ietf.group.views.streams") + else: + form = StreamEditForm(initial=dict(delegates=Email.objects.filter(role__group=group, role__name="delegate"))) + + return render_to_response('group/stream_edit.html', + {'group': group, + 'chairs': chairs, + 'form': form, + }, + context_instance=RequestContext(request)) + diff --git a/ietf/templates/group/stream_edit.html b/ietf/templates/group/stream_edit.html new file mode 100644 index 000000000..454cb3b1e --- /dev/null +++ b/ietf/templates/group/stream_edit.html @@ -0,0 +1,55 @@ +{% extends "base.html" %} + +{% block title %}Manage {{ group.name }} stream{% endblock %} + +{% block morecss %} +form.edit td { padding-bottom: .5em; } +{% endblock %} + +{% block pagehead %} + +{% endblock %} + +{% block content %} +{% load ietf_filters %} +

Manage {{ group.name }} stream

+ +

+ Chair{{ chairs|pluralize }}: + {% for chair in chairs %} + {{ chair.person.plain_name }} <{{ chair.address }}>{% if not forloop.last %}, {% endif %} + {% endfor %} +

+ +

Delegates can be assigned with permission to do the tasks of the +chair{{ chairs|pluralize }}. Note that in order to actually do so, the delegates need a +Datatracker account. New accounts can be +created here.

+ +
+ + {% for field in form.visible_fields %} + + + + + {% endfor %} + + + + +
{{ field.label_tag }}: {% if field.field.required %}*{% endif %}{{ field }} + {% if field.help_text %}
{{ field.help_text }}
{% endif %} + {{ field.errors }} +
+ Cancel + +
+
+{% endblock %} + +{% block content_end %} + + + +{% endblock %} From 2d902e28d15237caa4f5e7a13661271c8e6af606 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 24 Sep 2013 17:25:10 +0000 Subject: [PATCH 029/173] Add stream edit page for setting delegates instead of page in ietfworkflows, add streams_menu template tag for outputting the streams menu instead of the one in ietfworkflows, move base_wgmenu.html and base_leftmenu.html templates to base/ (together with base/streams_menu.html), get rid of "first" madness in base/left_menu.html which is now obsolete anyway because we have a Settings headline which is always first - Legacy-Id: 6255 --- ietf/doc/templatetags/streams_menu.py | 31 +++++++++++++++++++ ietf/doc/templatetags/wg_menu.py | 2 +- ietf/templates/base.html | 2 +- .../left_menu.html} | 24 +++++--------- ietf/templates/base/streams_menu.html | 7 +++++ .../{base_wgmenu.html => base/wg_menu.html} | 0 ietf/templates/group/stream_edit.html | 4 +-- 7 files changed, 50 insertions(+), 20 deletions(-) create mode 100644 ietf/doc/templatetags/streams_menu.py rename ietf/templates/{base_leftmenu.html => base/left_menu.html} (89%) create mode 100644 ietf/templates/base/streams_menu.html rename ietf/templates/{base_wgmenu.html => base/wg_menu.html} (100%) diff --git a/ietf/doc/templatetags/streams_menu.py b/ietf/doc/templatetags/streams_menu.py new file mode 100644 index 000000000..74d4d1cd8 --- /dev/null +++ b/ietf/doc/templatetags/streams_menu.py @@ -0,0 +1,31 @@ +from django import template +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse + +from ietf.ietfauth.utils import has_role +from ietf.group.models import Group +from ietf.name.models import StreamName + +register = template.Library() + +@register.inclusion_tag('base/streams_menu.html', takes_context=True) +def streams_menu(context): + user = context["request"].user + + editable_streams = [] + + if user.is_authenticated(): + streams = StreamName.objects.exclude(slug="legacy") + + if has_role(user, "Secretariat"): + editable_stream = streams + else: + acronyms = Group.objects.filter(acronym__in=(s.slug for s in streams), + role__name="chair", + role__person__user=user).distinct().values_list("acronym", flat=True) + + for s in streams: + if s.slug in acronyms: + editable_streams.append(s) + + return { 'editable_streams': streams } diff --git a/ietf/doc/templatetags/wg_menu.py b/ietf/doc/templatetags/wg_menu.py index 4f58eb6c1..079757734 100644 --- a/ietf/doc/templatetags/wg_menu.py +++ b/ietf/doc/templatetags/wg_menu.py @@ -61,7 +61,7 @@ class WgMenuNode(template.Node): areas = [a for a in areas if a.active_groups] - res = loader.render_to_string('base_wgmenu.html', {'areas':areas}) + res = loader.render_to_string('base/wg_menu.html', {'areas':areas}) cache.set('base_left_wgmenu', x, 30*60) return res diff --git a/ietf/templates/base.html b/ietf/templates/base.html index 9ec643469..aac217f9d 100644 --- a/ietf/templates/base.html +++ b/ietf/templates/base.html @@ -69,7 +69,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% include "base_leftmenu.html" %} +{% include "base/left_menu.html" %}
{% if version_num %} diff --git a/ietf/templates/base_leftmenu.html b/ietf/templates/base/left_menu.html similarity index 89% rename from ietf/templates/base_leftmenu.html rename to ietf/templates/base/left_menu.html index 645a95a9b..0d67d8c34 100644 --- a/ietf/templates/base_leftmenu.html +++ b/ietf/templates/base/left_menu.html @@ -32,15 +32,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> {% endcomment %} -{% load wg_menu %} -{% load ietf_filters ietf_streams community_tags %} +{% load wg_menu %} +{% load streams_menu %} {% load ietf_filters community_tags %}
  • Settings
  • {% if request.user.is_authenticated %}Manage Account{% else %}New Account{% endif %}
  • Options
  • {% if user|has_role:"Area Director" %} -
  • AD Dashboard
  • +
  • AD Dashboard
  • My Documents
  • Next Telechat
  • Discusses
  • @@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  • Working Groups
  • {% endif %} {% if user|has_role:"Secretariat" %} -
  • Secretariat
  • +
  • Secretariat
  • Telechat Dates
  • Management Items
  • Milestones
  • @@ -57,24 +57,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  • Working Groups
  • Sync discrepancies {% endif %} -{% if user %} -{% get_user_managed_streams user as stream_list %} -{% if stream_list %} -
  • Streams
  • - {% for stream in stream_list %} - {{ stream.name }} stream - {% endfor %} -{% endif %} -{% endif %} +{% streams_menu %} {% if user|has_role:"IANA" %} -
  • IANA
  • +
  • IANA
  • Sync discrepancies
  • {% endif %} {% if user|has_role:"RFC Editor" %} -
  • RFC Editor
  • +
  • RFC Editor
  • Sync discrepancies
  • {% endif %} -
  • Working Groups
  • +
  • Working Groups
    • diff --git a/ietf/templates/base/streams_menu.html b/ietf/templates/base/streams_menu.html new file mode 100644 index 000000000..d4ce5bf7b --- /dev/null +++ b/ietf/templates/base/streams_menu.html @@ -0,0 +1,7 @@ +{% load ietf_filters %} +{% if editable_streams %} +
    • Streams
    • + {% for stream in editable_streams %} + {{ stream.name }} stream + {% endfor %} +{% endif %} diff --git a/ietf/templates/base_wgmenu.html b/ietf/templates/base/wg_menu.html similarity index 100% rename from ietf/templates/base_wgmenu.html rename to ietf/templates/base/wg_menu.html diff --git a/ietf/templates/group/stream_edit.html b/ietf/templates/group/stream_edit.html index 454cb3b1e..b1661e84f 100644 --- a/ietf/templates/group/stream_edit.html +++ b/ietf/templates/group/stream_edit.html @@ -15,10 +15,10 @@ form.edit td { padding-bottom: .5em; }

      Manage {{ group.name }} stream

      - Chair{{ chairs|pluralize }}: + Chair{{ chairs|pluralize }}: {% for chair in chairs %} {{ chair.person.plain_name }} <{{ chair.address }}>{% if not forloop.last %}, {% endif %} - {% endfor %} + {% endfor %}

      Delegates can be assigned with permission to do the tasks of the From 8bc5df166b3f82a90c7b9e70c1546b14bb6a6703 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 24 Sep 2013 17:28:43 +0000 Subject: [PATCH 030/173] Get rid of unused ietfworkflows import - Legacy-Id: 6256 --- ietf/iesg/tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 459c3f3ef..3f1fbdf7a 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -10,7 +10,6 @@ from pyquery import PyQuery from ietf.idtracker.models import * from ietf.iesg.models import * from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized -from ietf.ietfworkflows.models import Stream class RescheduleOnAgendaTestCase(django.test.TestCase): fixtures = ['base', 'draft'] From 2e152bbc9c14234cfb5a82722a55730f74d8751c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 24 Sep 2013 17:32:41 +0000 Subject: [PATCH 031/173] Fix naming bug in streams_menu template tag - Legacy-Id: 6257 --- ietf/doc/templatetags/streams_menu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/doc/templatetags/streams_menu.py b/ietf/doc/templatetags/streams_menu.py index 74d4d1cd8..9aa294ee8 100644 --- a/ietf/doc/templatetags/streams_menu.py +++ b/ietf/doc/templatetags/streams_menu.py @@ -18,7 +18,7 @@ def streams_menu(context): streams = StreamName.objects.exclude(slug="legacy") if has_role(user, "Secretariat"): - editable_stream = streams + editable_streams.extend(streams) else: acronyms = Group.objects.filter(acronym__in=(s.slug for s in streams), role__name="chair", @@ -28,4 +28,4 @@ def streams_menu(context): if s.slug in acronyms: editable_streams.append(s) - return { 'editable_streams': streams } + return { 'editable_streams': editable_streams } From 525259a84ffc8dc63d1264bd4c155e8a164d9b62 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 24 Sep 2013 17:34:20 +0000 Subject: [PATCH 032/173] Remove unused ietfworkflows imports - Legacy-Id: 6258 --- ietf/doc/views_draft.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index d04b17f20..7d3cd472a 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -24,9 +24,6 @@ from ietf.utils.textupload import get_cleaned_text_file_content from ietf.person.forms import EmailsField from ietf.group.models import Group -from ietf.ietfworkflows.models import Stream -from ietf.ietfworkflows.utils import update_stream -from ietf.ietfworkflows.streams import get_stream_from_draft from ietf.ietfworkflows.accounts import can_edit_state from ietf.doc.models import * From f0eddecf6b9cb9319bd92db509870adf4619f4af Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 25 Sep 2013 11:43:23 +0000 Subject: [PATCH 033/173] Add test for stream management page - Legacy-Id: 6262 --- ietf/group/tests.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 ietf/group/tests.py diff --git a/ietf/group/tests.py b/ietf/group/tests.py new file mode 100644 index 000000000..7cfc735d4 --- /dev/null +++ b/ietf/group/tests.py @@ -0,0 +1,33 @@ +import os, shutil, datetime + +import django.test +from django.core.urlresolvers import reverse as urlreverse + +from pyquery import PyQuery + +from ietf.utils.mail import outbox +from ietf.utils.test_utils import login_testing_unauthorized +from ietf.utils.test_data import make_test_data + +from ietf.name.models import * +from ietf.group.models import * +from ietf.person.models import * + +class StreamTests(django.test.TestCase): + fixtures = ['names'] + + def test_stream_edit(self): + make_test_data() + + stream_acronym = "ietf" + + url = urlreverse("ietf.group.views.stream_edit", kwargs=dict(acronym=stream_acronym)) + login_testing_unauthorized(self, "secretary", url) + + # get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + + r = self.client.post(url, dict(delegates="ad2@ietf.org")) + self.assertEqual(r.status_code, 302) + self.assertTrue(Role.objects.filter(name="delegate", group__acronym=stream_acronym, email__address="ad2@ietf.org")) From 5ba00a9362178e519d1f46abfcc6e64ee317e25e Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 26 Sep 2013 09:48:37 +0000 Subject: [PATCH 034/173] Move stream views to views_stream.py - Legacy-Id: 6266 --- ietf/group/stream_urls.py | 11 ++++------- ietf/group/tests.py | 2 +- ietf/group/{views.py => views_stream.py} | 0 ietf/templates/base/left_menu.html | 6 +++--- ietf/templates/base/streams_menu.html | 2 +- ietf/templates/group/stream_edit.html | 2 +- 6 files changed, 10 insertions(+), 13 deletions(-) rename ietf/group/{views.py => views_stream.py} (100%) diff --git a/ietf/group/stream_urls.py b/ietf/group/stream_urls.py index 4af4c58a8..05f7731a9 100644 --- a/ietf/group/stream_urls.py +++ b/ietf/group/stream_urls.py @@ -2,14 +2,11 @@ from django.conf.urls.defaults import patterns, include -import views +import views_stream urlpatterns = patterns('', - (r'^$', views.streams), - (r'^(?P[a-zA-Z0-9-]+)/$', views.stream_documents, None), - (r'^(?P[a-zA-Z0-9-]+)/edit/$', views.stream_edit), -# (r'^(?P[a-zA-Z0-9-]+)/history/$', views.stream_history), -# (r'^(?P[a-zA-Z0-9-]+)/edit/$', views.stream_edit) + (r'^$', views_stream.streams), + (r'^(?P[a-zA-Z0-9-]+)/$', views_stream.stream_documents, None), + (r'^(?P[a-zA-Z0-9-]+)/edit/$', views_stream.stream_edit), (r'^management/', include('ietf.ietfworkflows.urls')), - ) diff --git a/ietf/group/tests.py b/ietf/group/tests.py index 7cfc735d4..db3e326e8 100644 --- a/ietf/group/tests.py +++ b/ietf/group/tests.py @@ -21,7 +21,7 @@ class StreamTests(django.test.TestCase): stream_acronym = "ietf" - url = urlreverse("ietf.group.views.stream_edit", kwargs=dict(acronym=stream_acronym)) + url = urlreverse("ietf.group.views_stream.stream_edit", kwargs=dict(acronym=stream_acronym)) login_testing_unauthorized(self, "secretary", url) # get diff --git a/ietf/group/views.py b/ietf/group/views_stream.py similarity index 100% rename from ietf/group/views.py rename to ietf/group/views_stream.py diff --git a/ietf/templates/base/left_menu.html b/ietf/templates/base/left_menu.html index 0d67d8c34..2ee2545ce 100644 --- a/ietf/templates/base/left_menu.html +++ b/ietf/templates/base/left_menu.html @@ -88,9 +88,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    • Streams:
      - IAB - IRTF - ISE + IAB + IRTF + ISE
    • Submit a draft
    • {% if user|in_group:"WG Chair" %} diff --git a/ietf/templates/base/streams_menu.html b/ietf/templates/base/streams_menu.html index d4ce5bf7b..c25a5175b 100644 --- a/ietf/templates/base/streams_menu.html +++ b/ietf/templates/base/streams_menu.html @@ -2,6 +2,6 @@ {% if editable_streams %}
    • Streams
    • {% for stream in editable_streams %} - {{ stream.name }} stream + {{ stream.name }} stream {% endfor %} {% endif %} diff --git a/ietf/templates/group/stream_edit.html b/ietf/templates/group/stream_edit.html index b1661e84f..7893bffee 100644 --- a/ietf/templates/group/stream_edit.html +++ b/ietf/templates/group/stream_edit.html @@ -40,7 +40,7 @@ Datatracker account. New accounts can be - Cancel + Cancel From 56ea114e31f8fcc9e194f474eba60698cdd21145 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 26 Sep 2013 09:48:59 +0000 Subject: [PATCH 035/173] Fix missing parameter to is_rgdelegate - Legacy-Id: 6267 --- ietf/ietfworkflows/accounts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/ietfworkflows/accounts.py b/ietf/ietfworkflows/accounts.py index c687bda85..5928628c6 100644 --- a/ietf/ietfworkflows/accounts.py +++ b/ietf/ietfworkflows/accounts.py @@ -126,7 +126,7 @@ def can_adopt(user, draft): person = get_person_for_user(user) if not person: return False - return is_wgchair(person) or is_rgchair(person) or is_wgdelegate(person) or is_rgdelegate() or is_secretariat(user) + return is_wgchair(person) or is_rgchair(person) or is_wgdelegate(person) or is_rgdelegate(person) or is_secretariat(user) else: return is_secretariat(user) From c760b48b389144c5efe589768c7fe9c1ba206222 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 26 Sep 2013 13:00:25 +0000 Subject: [PATCH 036/173] Make streams_menu tag more robust in the face of "request" missing from context, this would normally be an error but it can happen if a 404 is returned - Legacy-Id: 6268 --- ietf/doc/templatetags/streams_menu.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ietf/doc/templatetags/streams_menu.py b/ietf/doc/templatetags/streams_menu.py index 9aa294ee8..82f62620e 100644 --- a/ietf/doc/templatetags/streams_menu.py +++ b/ietf/doc/templatetags/streams_menu.py @@ -1,6 +1,7 @@ from django import template from django.conf import settings from django.core.urlresolvers import reverse as urlreverse +from django.contrib.auth.models import AnonymousUser from ietf.ietfauth.utils import has_role from ietf.group.models import Group @@ -10,10 +11,10 @@ register = template.Library() @register.inclusion_tag('base/streams_menu.html', takes_context=True) def streams_menu(context): - user = context["request"].user - editable_streams = [] + user = context["request"].user if "request" in context else AnonymousUser() + if user.is_authenticated(): streams = StreamName.objects.exclude(slug="legacy") From c508a5672dcda354e6a9b7ba8868a061def46341 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 26 Sep 2013 13:14:07 +0000 Subject: [PATCH 037/173] Reimplement WG/RG adoption of drafts with the new schema, move it to doc/ together with test and utilities, rewrite the UI to be more in line with the rest of the edit pages (including the revamped stream state change UI) - Legacy-Id: 6269 --- ietf/doc/mails.py | 31 ++++++ ietf/doc/tests_draft.py | 38 ++++++++ ietf/doc/urls.py | 2 + ietf/doc/utils.py | 41 +++++++- ietf/doc/views_doc.py | 9 +- ietf/doc/views_draft.py | 96 +++++++++++++++++++ ietf/templates/doc/draft/adopt_draft.html | 43 +++++++++ .../doc/mail/stream_state_changed_email.txt | 8 ++ 8 files changed, 260 insertions(+), 8 deletions(-) create mode 100644 ietf/templates/doc/draft/adopt_draft.html create mode 100644 ietf/templates/doc/mail/stream_state_changed_email.txt diff --git a/ietf/doc/mails.py b/ietf/doc/mails.py index 7f483ad5e..65bce6d11 100644 --- a/ietf/doc/mails.py +++ b/ietf/doc/mails.py @@ -413,3 +413,34 @@ def email_last_call_expired(doc): doc=doc, url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()), cc="iesg-secretary@ietf.org") + +def stream_state_email_recipients(doc, extra_recipients): + persons = set() + res = [] + for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")).select_related("person", "email"): + res.append(r.formatted_email()) + persons.add(r.person) + + for email in doc.authors.all(): + if email.person not in persons: + res.append(email.formatted_email()) + persons.add(email.person) + + for x in extra_recipients: + if not x in res: + res.append(x) + + return res + +def email_stream_state_changed(request, doc, prev_state, new_state, changed_by, comment="", extra_recipients=[]): + recipients = stream_state_email_recipients(doc, extra_recipients) + + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, + u"Stream State Changed for Draft %s" % doc.name, + 'doc/mail/stream_state_changed_email.txt', + dict(doc=doc, + url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + prev_state=prev_state, + new_state=new_state, + changed_by=changed_by, + comment=comment)) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 03ca24c72..6e2cbfad6 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -983,3 +983,41 @@ class RequestPublicationTestCase(django.test.TestCase): # the IANA copy self.assertTrue("Document Action" in outbox[-1]['Subject']) self.assertTrue(not outbox[-1]['CC']) + +class AdoptDraftTests(django.test.TestCase): + fixtures = ['names'] + + def test_adopt_document(self): + draft = make_test_data() + draft.stream = None + draft.group = Group.objects.get(type="individ") + draft.save() + draft.unset_state("draft-stream-ietf") + + url = urlreverse('doc_adopt_draft', kwargs=dict(name=draft.name)) + login_testing_unauthorized(self, "marschairman", url) + + # get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form select[name="group"] option')), 1) # we can only select "mars" + + # adopt in mars WG + mailbox_before = len(outbox) + events_before = draft.docevent_set.count() + r = self.client.post(url, + dict(comment="some comment", + group=Group.objects.get(acronym="mars").pk, + weeks="10")) + self.assertEquals(r.status_code, 302) + + draft = Document.objects.get(pk=draft.pk) + self.assertEquals(draft.group.acronym, "mars") + self.assertEquals(draft.stream_id, "ietf") + self.assertEquals(draft.docevent_set.count() - events_before, 4) + self.assertEquals(len(outbox), mailbox_before + 1) + self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) + self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) + self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) + diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index 49df3de21..6453de180 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -86,6 +86,8 @@ urlpatterns += patterns('', url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherd/$', views_draft.edit_shepherd, name='doc_edit_shepherd'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherdwriteup/$', views_draft.edit_shepherd_writeup, name='doc_edit_shepherd_writeup'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/requestpublication/$', views_draft.request_publication, name='doc_request_publication'), + url(r'^(?P[A-Za-z0-9._+-]+)/edit/adopt/$', views_draft.adopt_draft, name='doc_adopt_draft'), + url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/stream/$', views_draft.change_stream_state, name='doc_change_stream_state'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/clearballot/$', views_ballot.clear_ballot, name='doc_clear_ballot'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/deferballot/$', views_ballot.defer_ballot, name='doc_defer_ballot'), diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index 7798969f9..d12699b14 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -5,6 +5,8 @@ from django.conf import settings from ietf.utils import markup_txt from ietf.doc.models import * +from ietf.group.models import Role +from ietf.ietfauth.utils import has_role def get_state_types(doc): res = [] @@ -37,6 +39,20 @@ def get_tags_for_stream_id(stream_id): else: return [] +def can_adopt_draft(user, doc): + if not user.is_authenticated(): + return False + + if has_role(user, "Secretariat"): + return True + + return (doc.stream_id in (None, "ietf", "irtf") + and doc.group.type_id == "individ" + and Role.objects.filter(name__in=("chair", "delegate", "secr"), + group__type__in=("wg", "rg"), + group__state="active", + person__user=user).exists()) + def needed_ballot_positions(doc, active_positions): '''Returns text answering the question "what does this document need to pass?". The return value is only useful if the document @@ -225,7 +241,30 @@ def add_state_change_event(doc, by, prev_state, new_state, timestamp=None): e.time = timestamp e.save() return e - + +def update_reminder(doc, reminder_type_slug, event, due_date): + reminder_type = DocReminderTypeName.objects.get(slug=reminder_type_slug) + + try: + reminder = DocReminder.objects.get(event__doc=doc, type=reminder_type, active=True) + except DocReminder.DoesNotExist: + reminder = None + + if due_date: + # activate/update reminder + if not reminder: + reminder = DocReminder(type=reminder_type) + + reminder.event = event + reminder.due = due_date + reminder.active = True + reminder.save() + else: + # deactivate reminder + if reminder: + reminder.active = False + reminder.save() + def prettify_std_name(n): if re.match(r"(rfc|bcp|fyi|std)[0-9]+", n): return n[:3].upper() + " " + n[3:] diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 6328fcc1c..fe5a42214 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -290,13 +290,8 @@ def document_main(request, name, rev=None): # remaining actions actions = [] - if ((not doc.stream_id or doc.stream_id in ("ietf", "irtf")) and group.type_id == "individ" and - (request.user.is_authenticated() and - Role.objects.filter(person__user=request.user, name__in=("chair", "secr", "delegate"), - group__type__in=("wg","rg"), - group__state="active") - or has_role(request.user, "Secretariat"))): - actions.append(("Adopt in Group", urlreverse('edit_adopt', kwargs=dict(name=doc.name)))) + if can_adopt_draft(request.user, doc): + actions.append(("Adopt in Group", urlreverse('doc_adopt_draft', kwargs=dict(name=doc.name)))) if doc.get_state_slug() == "expired" and not resurrected_by and can_edit: actions.append(("Request Resurrect", urlreverse('doc_request_resurrect', kwargs=dict(name=doc.name)))) diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index 7d3cd472a..e12d3f918 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -1100,3 +1100,99 @@ def request_publication(request, name): ), context_instance = RequestContext(request)) +class AdoptDraftForm(forms.Form): + group = forms.ModelChoiceField(queryset=Group.objects.filter(type__in=["wg", "rg"], state="active").order_by("-type", "acronym"), required=True, empty_label=None) + comment = forms.CharField(widget=forms.Textarea, required=False, label="Comment", help_text="Optional comment explaining the reasons for the adoption") + weeks = forms.IntegerField(required=False, label="Expected weeks in adoption state") + + def __init__(self, *args, **kwargs): + user = kwargs.pop("user") + + super(AdoptDraftForm, self).__init__(*args, **kwargs) + + if has_role(user, "Secretariat"): + pass # all groups + else: + self.fields["group"].queryset = self.fields["group"].queryset.filter(role__person__user=user, role__name__in=("chair", "delegate", "secr")).distinct() + + self.fields['group'].choices = [(g.pk, '%s - %s' % (g.acronym, g.name)) for g in self.fields["group"].queryset] + + +@login_required +def adopt_draft(request, name): + doc = get_object_or_404(Document, type="draft", name=name) + + if not can_adopt_draft(request.user, doc): + return HttpResponseForbidden("You don't have permission to access this view") + + if request.method == 'POST': + form = AdoptDraftForm(request.POST, user=request.user) + + if form.is_valid(): + # adopt + by = request.user.get_profile() + + save_document_in_history(doc) + + doc.time = datetime.datetime.now() + + group = form.cleaned_data["group"] + comment = form.cleaned_data["comment"].strip() + + if group.type.slug == "rg": + new_stream = StreamName.objects.get(slug="irtf") + adopt_state_slug = "active" + else: + new_stream = StreamName.objects.get(slug="ietf") + adopt_state_slug = "c-adopt" + + if doc.stream != new_stream: + e = DocEvent(type="changed_stream", time=doc.time, by=by, doc=doc) + e.desc = u"Changed stream to %s" % new_stream.name + if doc.stream: + e.desc += u" from %s" % doc.stream.name + e.save() + doc.stream = new_stream + + if group != doc.group: + e = DocEvent(type="changed_group", time=doc.time, by=by, doc=doc) + e.desc = u"Changed group to %s (%s)" % (group.name, group.acronym.upper()) + if doc.group.type_id != "individ": + e.desc += " from %s (%s)" % (doc.group.name, doc.group.acronym.upper()) + e.save() + doc.group = group + + doc.save() + + prev_state = doc.get_state("draft-stream-%s" % doc.stream_id) + new_state = State.objects.get(slug=adopt_state_slug, type="draft-stream-%s" % doc.stream_id, used=True) + + if new_state != prev_state: + doc.set_state(new_state) + e = add_state_change_event(doc, by, prev_state, new_state, doc.time) + + due_date = None + if form.cleaned_data["weeks"] != None: + due_date = datetime.date.today() + datetime.timedelta(weeks=form.cleaned_data["weeks"]) + + update_reminder(doc, "stream-s", e, due_date) + + email_stream_state_changed(request, doc, prev_state, new_state, by, comment) + + if comment: + e = DocEvent(type="added_comment", time=doc.time, by=by, doc=doc) + e.desc = comment + e.save() + + return HttpResponseRedirect(doc.get_absolute_url()) + else: + form = AdoptDraftForm(user=request.user) + + return render_to_response('doc/draft/adopt_draft.html', + {'doc': doc, + 'form': form, + }, + context_instance=RequestContext(request)) + +def change_stream_state(request): + pass diff --git a/ietf/templates/doc/draft/adopt_draft.html b/ietf/templates/doc/draft/adopt_draft.html new file mode 100644 index 000000000..062a13799 --- /dev/null +++ b/ietf/templates/doc/draft/adopt_draft.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} + +{% block title %}Adopt {{ doc }} in Group{% endblock %} + +{% block morecss %} +form.adopt-draft th { width: 8em; } +form.adopt-draft #id_comment { width: 30em; } +form.adopt-draft #id_weeks { width: 3em; } +form.adopt-draft .actions { text-align: right; padding-top: 1em; } +p.intro { max-width: 50em; } +{% endblock %} + +{% block content %} +

      Adopt {{ doc }} in Group

      + +

      You can adopt this draft into a group.

      + +

      For a WG, the draft enters the IETF stream and the +stream state becomes "Call for Adoption by WG Issued". For an RG, the +draft enters the IRTF stream and the stream state becomes "Active RG +Document".

      + +
      + {% for field in form.hidden_fields %}{{ field }}{% endfor %} + + {% for field in form.visible_fields %} + + + + + {% endfor %} + + + +
      {{ field.label_tag }}:{{ field }} + {% if field.help_text %}
      {{ field.help_text }}
      {% endif %} + {{ field.errors }} +
      + Cancel + +
      +
      +{% endblock %} diff --git a/ietf/templates/doc/mail/stream_state_changed_email.txt b/ietf/templates/doc/mail/stream_state_changed_email.txt new file mode 100644 index 000000000..84378acd7 --- /dev/null +++ b/ietf/templates/doc/mail/stream_state_changed_email.txt @@ -0,0 +1,8 @@ +{% autoescape off %}{% filter wordwrap:73 %} +The stream state of {{ doc }} has been changed{% if prev_state %} from {{ prev_state.name }}{% endif %} to {{ new_state.name }} by {{ changed_by }}. +{% if comment %} + +Comment: +{{ comment }} +{% endif %} +{% endfilter %}{% endautoescape %} From f1e0be1033a143985b50a7642d900036acb6419d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 26 Sep 2013 15:03:55 +0000 Subject: [PATCH 038/173] Improve the stream state change email a bit, switch to using a specific message for adoptions with the name of the WG/RG - Legacy-Id: 6270 --- ietf/doc/mails.py | 19 ++++++++++++++++--- ietf/doc/tests_draft.py | 3 +-- ietf/doc/views_draft.py | 4 ++-- .../doc/mail/draft_adopted_email.txt | 8 ++++++++ .../doc/mail/stream_state_changed_email.txt | 8 ++++---- 5 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 ietf/templates/doc/mail/draft_adopted_email.txt diff --git a/ietf/doc/mails.py b/ietf/doc/mails.py index 65bce6d11..2298fd024 100644 --- a/ietf/doc/mails.py +++ b/ietf/doc/mails.py @@ -432,15 +432,28 @@ def stream_state_email_recipients(doc, extra_recipients): return res -def email_stream_state_changed(request, doc, prev_state, new_state, changed_by, comment="", extra_recipients=[]): +def email_stream_state_changed(request, doc, prev_state, new_state, by, comment="", extra_recipients=[]): recipients = stream_state_email_recipients(doc, extra_recipients) + state_type = (prev_state or new_state).type + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, - u"Stream State Changed for Draft %s" % doc.name, + u"%s changed for %s" % (state_type.label, doc.name), 'doc/mail/stream_state_changed_email.txt', dict(doc=doc, url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + state_type=state_type, prev_state=prev_state, new_state=new_state, - changed_by=changed_by, + by=by, + comment=comment)) + +def email_draft_adopted(request, doc, by, comment): + recipients = stream_state_email_recipients(doc, []) + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, + u"%s adopted in %s %s" % (doc.name, doc.group.acronym, doc.group.type.name), + 'doc/mail/draft_adopted_email.txt', + dict(doc=doc, + url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + by=by, comment=comment)) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 6e2cbfad6..45e489c09 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -1017,7 +1017,6 @@ class AdoptDraftTests(django.test.TestCase): self.assertEquals(draft.stream_id, "ietf") self.assertEquals(draft.docevent_set.count() - events_before, 4) self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) + self.assertTrue("adopted" in outbox[-1]["Subject"].lower()) self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) - diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index e12d3f918..03c8bab02 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -1177,13 +1177,13 @@ def adopt_draft(request, name): update_reminder(doc, "stream-s", e, due_date) - email_stream_state_changed(request, doc, prev_state, new_state, by, comment) - if comment: e = DocEvent(type="added_comment", time=doc.time, by=by, doc=doc) e.desc = comment e.save() + email_draft_adopted(request, doc, by, comment) + return HttpResponseRedirect(doc.get_absolute_url()) else: form = AdoptDraftForm(user=request.user) diff --git a/ietf/templates/doc/mail/draft_adopted_email.txt b/ietf/templates/doc/mail/draft_adopted_email.txt new file mode 100644 index 000000000..1e01fb13d --- /dev/null +++ b/ietf/templates/doc/mail/draft_adopted_email.txt @@ -0,0 +1,8 @@ +{% autoescape off %}{% filter wordwrap:73 %} +The document {{ doc }} has been adopted in the {{ doc.group.acronym }} {{ doc.group.type.name }} by {{ by }}: + +{{ url }} +{% if comment %} + +Comment: +{{ comment }}{% endif %}{% endfilter %}{% endautoescape %} diff --git a/ietf/templates/doc/mail/stream_state_changed_email.txt b/ietf/templates/doc/mail/stream_state_changed_email.txt index 84378acd7..09f9ba922 100644 --- a/ietf/templates/doc/mail/stream_state_changed_email.txt +++ b/ietf/templates/doc/mail/stream_state_changed_email.txt @@ -1,8 +1,8 @@ {% autoescape off %}{% filter wordwrap:73 %} -The stream state of {{ doc }} has been changed{% if prev_state %} from {{ prev_state.name }}{% endif %} to {{ new_state.name }} by {{ changed_by }}. +The {{ state_type.label }} of {{ doc }} has been changed to "{{ new_state.name }}"{% if prev_state %} from "{{ prev_state.name }}"{% endif %} by {{ by }}: + +{{ url }} {% if comment %} Comment: -{{ comment }} -{% endif %} -{% endfilter %}{% endautoescape %} +{{ comment }}{% endif %}{% endfilter %}{% endautoescape %} From 8c88bc5aec4fa7e8ea1f3b003c4f06fc46a05757 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 27 Sep 2013 14:19:27 +0000 Subject: [PATCH 039/173] Rewrite change stream state page, moving it to doc/views_draft.py, port associated tests, make the recommended next states clickable with Javascript so a standard state change is just two clicks (next state and save button) - Legacy-Id: 6288 --- ietf/doc/mails.py | 43 ++++-- ietf/doc/tests_draft.py | 102 ++++++++++++- ietf/doc/views_draft.py | 138 +++++++++++++++++- .../doc/draft/change_stream_state.html | 73 +++++++++ .../doc/mail/stream_tags_changed_email.txt | 10 ++ 5 files changed, 341 insertions(+), 25 deletions(-) create mode 100644 ietf/templates/doc/draft/change_stream_state.html create mode 100644 ietf/templates/doc/mail/stream_tags_changed_email.txt diff --git a/ietf/doc/mails.py b/ietf/doc/mails.py index 2298fd024..54242c747 100644 --- a/ietf/doc/mails.py +++ b/ietf/doc/mails.py @@ -9,7 +9,7 @@ from django.core.urlresolvers import reverse as urlreverse from ietf.utils.mail import send_mail, send_mail_text from ietf.ipr.search import iprs_from_docs, related_docs -from ietf.doc.models import WriteupDocEvent, BallotPositionDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent +from ietf.doc.models import WriteupDocEvent, BallotPositionDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent, DocTagName from ietf.person.models import Person from ietf.group.models import Group, Role @@ -414,7 +414,7 @@ def email_last_call_expired(doc): url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()), cc="iesg-secretary@ietf.org") -def stream_state_email_recipients(doc, extra_recipients): +def stream_state_email_recipients(doc, extra_recipients=[]): persons = set() res = [] for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")).select_related("person", "email"): @@ -426,14 +426,25 @@ def stream_state_email_recipients(doc, extra_recipients): res.append(email.formatted_email()) persons.add(email.person) - for x in extra_recipients: - if not x in res: - res.append(x) + for p in extra_recipients: + if not p in persons: + res.append(p.formatted_email()) + persons.add(p) return res - -def email_stream_state_changed(request, doc, prev_state, new_state, by, comment="", extra_recipients=[]): - recipients = stream_state_email_recipients(doc, extra_recipients) + +def email_draft_adopted(request, doc, by, comment): + recipients = stream_state_email_recipients(doc) + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, + u"%s adopted in %s %s" % (doc.name, doc.group.acronym, doc.group.type.name), + 'doc/mail/draft_adopted_email.txt', + dict(doc=doc, + url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + by=by, + comment=comment)) + +def email_stream_state_changed(request, doc, prev_state, new_state, by, comment=""): + recipients = stream_state_email_recipients(doc) state_type = (prev_state or new_state).type @@ -448,12 +459,20 @@ def email_stream_state_changed(request, doc, prev_state, new_state, by, comment= by=by, comment=comment)) -def email_draft_adopted(request, doc, by, comment): - recipients = stream_state_email_recipients(doc, []) +def email_stream_tags_changed(request, doc, added_tags, removed_tags, by, comment=""): + extra_recipients = [] + + if DocTagName.objects.get(slug="sheph-u") in added_tags and doc.shepherd: + extra_recipients.append(doc.shepherd) + + recipients = stream_state_email_recipients(doc, extra_recipients) + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, - u"%s adopted in %s %s" % (doc.name, doc.group.acronym, doc.group.type.name), - 'doc/mail/draft_adopted_email.txt', + u"Tags changed for %s" % doc.name, + 'doc/mail/stream_tags_changed_email.txt', dict(doc=doc, url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + added=added_tags, + removed=removed_tags, by=by, comment=comment)) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 45e489c09..5e7e9a3d7 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -11,6 +11,7 @@ from pyquery import PyQuery import debug from ietf.doc.models import * +from ietf.doc .utils import * from ietf.name.models import * from ietf.group.models import * from ietf.person.models import * @@ -999,9 +1000,9 @@ class AdoptDraftTests(django.test.TestCase): # get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('form select[name="group"] option')), 1) # we can only select "mars" + self.assertEqual(len(q('form select[name="group"] option')), 1) # we can only select "mars" # adopt in mars WG mailbox_before = len(outbox) @@ -1010,13 +1011,100 @@ class AdoptDraftTests(django.test.TestCase): dict(comment="some comment", group=Group.objects.get(acronym="mars").pk, weeks="10")) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.group.acronym, "mars") - self.assertEquals(draft.stream_id, "ietf") - self.assertEquals(draft.docevent_set.count() - events_before, 4) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(draft.group.acronym, "mars") + self.assertEqual(draft.stream_id, "ietf") + self.assertEqual(draft.docevent_set.count() - events_before, 4) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("adopted" in outbox[-1]["Subject"].lower()) self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) + +class ChangeStreamStateTests(django.test.TestCase): + fixtures = ['names'] + + def test_set_tags(self): + draft = make_test_data() + draft.tags = DocTagName.objects.filter(slug="w-expert") + draft.group.unused_tags.add("w-refdoc") + + url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name)) + login_testing_unauthorized(self, "marschairman", url) + + # get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + # make sure the unused tags are hidden + unused = draft.group.unused_tags.values_list("slug", flat=True) + for t in q("input[name=tags]"): + self.assertTrue(t.attrib["value"] not in unused) + + # set tags + mailbox_before = len(outbox) + events_before = draft.docevent_set.count() + r = self.client.post(url, + dict(new_state=draft.get_state("draft-stream-%s" % draft.stream_id).pk, + comment="some comment", + weeks="10", + tags=["need-aut", "sheph-u"], + )) + self.assertEqual(r.status_code, 302) + + draft = Document.objects.get(pk=draft.pk) + self.assertEqual(draft.tags.count(), 2) + self.assertEqual(draft.tags.filter(slug="w-expert").count(), 0) + self.assertEqual(draft.tags.filter(slug="need-aut").count(), 1) + self.assertEqual(draft.tags.filter(slug="sheph-u").count(), 1) + self.assertEqual(draft.docevent_set.count() - events_before, 2) + self.assertEqual(len(outbox), mailbox_before + 1) + self.assertTrue("tags changed" in outbox[-1]["Subject"].lower()) + self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) + self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) + self.assertTrue("plain@example.com" in unicode(outbox[-1])) + + def test_set_state(self): + draft = make_test_data() + + url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name)) + login_testing_unauthorized(self, "marschairman", url) + + # get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + # make sure the unused states are hidden + unused = draft.group.unused_states.values_list("pk", flat=True) + for t in q("select[name=new_state]").find("option[name=tags]"): + self.assertTrue(t.attrib["value"] not in unused) + self.assertEqual(len(q('select[name=new_state]')), 1) + + # set new state + old_state = draft.get_state("draft-stream-%s" % draft.stream_id ) + new_state = State.objects.get(used=True, type="draft-stream-%s" % draft.stream_id, slug="parked") + self.assertNotEqual(old_state, new_state) + mailbox_before = len(outbox) + events_before = draft.docevent_set.count() + + r = self.client.post(url, + dict(new_state=new_state.pk, + comment="some comment", + weeks="10", + tags=[t.pk for t in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))], + )) + self.assertEqual(r.status_code, 302) + + draft = Document.objects.get(pk=draft.pk) + self.assertEqual(draft.get_state("draft-stream-%s" % draft.stream_id), new_state) + self.assertEqual(draft.docevent_set.count() - events_before, 2) + reminder = DocReminder.objects.filter(event__doc=draft, type="stream-s") + self.assertEqual(len(reminder), 1) + due = datetime.datetime.now() + datetime.timedelta(weeks=10) + self.assertTrue(due - datetime.timedelta(days=1) <= reminder[0].due <= due + datetime.timedelta(days=1)) + self.assertEqual(len(outbox), mailbox_before + 1) + self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) + self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) + self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) + diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index 03c8bab02..7386750e2 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -13,6 +13,7 @@ from django.db.models import Max from django.conf import settings from django.forms.util import ErrorList from django.contrib.auth.decorators import login_required +from django.template.defaultfilters import pluralize from ietf.utils.mail import send_mail_text, send_mail_message from ietf.ietfauth.decorators import role_required @@ -1123,7 +1124,7 @@ def adopt_draft(request, name): doc = get_object_or_404(Document, type="draft", name=name) if not can_adopt_draft(request.user, doc): - return HttpResponseForbidden("You don't have permission to access this view") + return HttpResponseForbidden("You don't have permission to access this page") if request.method == 'POST': form = AdoptDraftForm(request.POST, user=request.user) @@ -1137,8 +1138,6 @@ def adopt_draft(request, name): doc.time = datetime.datetime.now() group = form.cleaned_data["group"] - comment = form.cleaned_data["comment"].strip() - if group.type.slug == "rg": new_stream = StreamName.objects.get(slug="irtf") adopt_state_slug = "active" @@ -1146,6 +1145,7 @@ def adopt_draft(request, name): new_stream = StreamName.objects.get(slug="ietf") adopt_state_slug = "c-adopt" + # stream if doc.stream != new_stream: e = DocEvent(type="changed_stream", time=doc.time, by=by, doc=doc) e.desc = u"Changed stream to %s" % new_stream.name @@ -1154,6 +1154,7 @@ def adopt_draft(request, name): e.save() doc.stream = new_stream + # group if group != doc.group: e = DocEvent(type="changed_group", time=doc.time, by=by, doc=doc) e.desc = u"Changed group to %s (%s)" % (group.name, group.acronym.upper()) @@ -1164,9 +1165,9 @@ def adopt_draft(request, name): doc.save() + # state prev_state = doc.get_state("draft-stream-%s" % doc.stream_id) new_state = State.objects.get(slug=adopt_state_slug, type="draft-stream-%s" % doc.stream_id, used=True) - if new_state != prev_state: doc.set_state(new_state) e = add_state_change_event(doc, by, prev_state, new_state, doc.time) @@ -1177,6 +1178,8 @@ def adopt_draft(request, name): update_reminder(doc, "stream-s", e, due_date) + # comment + comment = form.cleaned_data["comment"].strip() if comment: e = DocEvent(type="added_comment", time=doc.time, by=by, doc=doc) e.desc = comment @@ -1194,5 +1197,128 @@ def adopt_draft(request, name): }, context_instance=RequestContext(request)) -def change_stream_state(request): - pass +class ChangeStreamStateForm(forms.Form): + new_state = forms.ModelChoiceField(queryset=State.objects.filter(used=True), label='State') + weeks = forms.IntegerField(label='Expected weeks in state',required=False) + comment = forms.CharField(widget=forms.Textarea, required=False, help_text="Optional comment for the document history") + tags = forms.ModelMultipleChoiceField(queryset=DocTagName.objects.filter(used=True), widget=forms.CheckboxSelectMultiple, required=False) + + def __init__(self, *args, **kwargs): + doc = kwargs.pop("doc") + state_type = kwargs.pop("state_type") + super(ChangeStreamStateForm, self).__init__(*args, **kwargs) + + f = self.fields["new_state"] + f.queryset = f.queryset.filter(type=state_type) + if doc.group: + unused_states = doc.group.unused_states.values_list("pk", flat=True) + f.queryset = f.queryset.exclude(pk__in=unused_states) + f.label = state_type.label + + f = self.fields['tags'] + f.queryset = f.queryset.filter(slug__in=get_tags_for_stream_id(doc.stream_id)) + if doc.group: + unused_tags = doc.group.unused_tags.values_list("pk", flat=True) + f.queryset = f.queryset.exclude(pk__in=unused_tags) + +def next_states_for_stream_state(doc, state_type, current_state): + # find next states + next_states = [] + if current_state: + next_states = current_state.next_states.all() + + if doc.stream_id == "ietf" and doc.group: + transitions = doc.group.groupstatetransitions_set.filter(state=current_state) + if transitions: + next_states = transitions[0].next_states.all() + else: + # return the initial state + states = State.objects.filter(used=True, type=state_type).order_by('order') + if states: + next_states = states[:1] + + if doc.group: + unused_states = doc.group.unused_states.values_list("pk", flat=True) + next_states = [n for n in next_states if n.pk not in unused_states] + + return next_states + +@login_required +def change_stream_state(request, name): + doc = get_object_or_404(Document, type="draft", name=name) + if not doc.stream: + raise Http404 + + if not is_authorized_in_doc_stream(request.user, doc): + return HttpResponseForbidden("You don't have permission to access this page") + + state_type = StateType.objects.get(slug="draft-stream-%s" % doc.stream_id) + prev_state = doc.get_state(state_type.slug) + next_states = next_states_for_stream_state(doc, state_type, prev_state) + + if request.method == 'POST': + form = ChangeStreamStateForm(request.POST, doc=doc, state_type=state_type) + if form.is_valid(): + by = request.user.get_profile() + + save_document_in_history(doc) + + doc.time = datetime.datetime.now() + comment = form.cleaned_data["comment"].strip() + + # state + new_state = form.cleaned_data["new_state"] + if new_state != prev_state: + doc.set_state(new_state) + e = add_state_change_event(doc, by, prev_state, new_state, doc.time) + + due_date = None + if form.cleaned_data["weeks"] != None: + due_date = datetime.date.today() + datetime.timedelta(weeks=form.cleaned_data["weeks"]) + + update_reminder(doc, "stream-s", e, due_date) + + email_stream_state_changed(request, doc, prev_state, new_state, by, comment) + + # tags + existing_tags = set(doc.tags.all()) + new_tags = set(form.cleaned_data["tags"]) + + if existing_tags != new_tags: + doc.tags = new_tags + + e = DocEvent(type="changed_document", time=doc.time, by=by, doc=doc) + added_tags = new_tags - existing_tags + removed_tags = existing_tags - new_tags + l = [] + if added_tags: + l.append(u"Tag%s %s set." % (pluralize(added_tags), ", ".join(t.name for t in added_tags))) + if removed_tags: + l.append(u"Tag%s %s cleared." % (pluralize(removed_tags), ", ".join(t.name for t in removed_tags))) + e.desc = " ".join(l) + e.save() + + email_stream_tags_changed(request, doc, added_tags, removed_tags, by, comment) + + # comment + if comment: + e = DocEvent(type="added_comment", time=doc.time, by=by, doc=doc) + e.desc = comment + e.save() + + return HttpResponseRedirect(doc.get_absolute_url()) + else: + form = ChangeStreamStateForm(initial=dict(new_state=prev_state.pk), + doc=doc, state_type=state_type) + + milestones = doc.groupmilestone_set.all() + + + return render_to_response("doc/draft/change_stream_state.html", + {"doc": doc, + "form": form, + "milestones": milestones, + "state_type": state_type, + "next_states": next_states, + }, + context_instance=RequestContext(request)) diff --git a/ietf/templates/doc/draft/change_stream_state.html b/ietf/templates/doc/draft/change_stream_state.html new file mode 100644 index 000000000..172a20b8e --- /dev/null +++ b/ietf/templates/doc/draft/change_stream_state.html @@ -0,0 +1,73 @@ +{% extends "base.html" %} + +{% block title %}Change {{ state_type.label }} of {{ doc }}{% endblock %} + +{% block morecss %} +.next-states { margin-bottom: 1em; } +.next-states a { + display: inline-block; + margin: 0 0.2em; + padding: 0.2em 0.3em; + border-radius: 0.2em; + cursor: pointer; + color: #333; + font-weight: bold; +} +.next-states a:hover { background-color: #eee; transition-duration: 0.2s; } +form.change-state th { max-width: 8em; } +form.change-state th, form.change-state td { padding-bottom: 0.3em; } +form.change-state #id_comment { width: 30em; } +form.change-state #id_weeks { width: 2em;} +form.change-state .actions { text-align: right; padding-top: 1em; } +form.change-state ul { padding: 0; margin: 0; } +form.change-state ul li { padding: 0; padding-bottom: 0.1em; margin: 0; list-style-type: none; } +form.change-state ul li input { vertical-align: middle; } +form.change-state ul li label { cursor: pointer; } +{% endblock %} + +{% block content %} +

      Change {{ state_type.label }} of {{ doc }}

      + +

      For help on the states, see the state table.

      + +{% if next_states %} +
      + Recommended next state{{next_states|pluralize}}: + {% for state in next_states %}{{ state.name }} {% if not forloop.last %} or {% endif %}{% endfor %} +
      +{% endif %} + +
      + + {% for field in form.visible_fields %} + + + + + {% endfor %} + + + +
      {{ field.label_tag }}:{{ field }} + {% if field.help_text %}
      {{ field.help_text }}
      {% endif %} + + {{ field.errors }} +
      + Cancel + +
      +
      +{% endblock %} + +{% block js %} + +{% endblock %} diff --git a/ietf/templates/doc/mail/stream_tags_changed_email.txt b/ietf/templates/doc/mail/stream_tags_changed_email.txt new file mode 100644 index 000000000..99b34b134 --- /dev/null +++ b/ietf/templates/doc/mail/stream_tags_changed_email.txt @@ -0,0 +1,10 @@ +{% autoescape off %}{% filter wordwrap:73 %} +The tags on {{ doc }} have been changed by {{ by }}: +{{ url }} + +{% if added %}Tag{{ added|pluralize }} {% for t in added %}"{{ t }}"{% if not forloop.last %}, {% endif %}{% endfor %} added.{% endif %} +{% if removed %}Tag{{ removed|pluralize }} {% for t in removed %}"{{ t }}"{% if not forloop.last %}, {% endif %}{% endfor %} cleared.{% endif %} +{% if comment %} + +Comment: +{{ comment }}{% endif %}{% endfilter %}{% endautoescape %} From 74966fac885c97f9ccdbbaa43653b8a1cef9633d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sat, 28 Sep 2013 18:54:43 +0000 Subject: [PATCH 040/173] Fix URL schema of change stream state to use the state type (to be more in line with the IANA ones), remove debug output, link to the new change state page from the draft main page - Legacy-Id: 6295 --- ietf/doc/tests_draft.py | 4 ++-- ietf/doc/urls.py | 2 +- ietf/doc/views_doc.py | 5 ++++- ietf/doc/views_draft.py | 9 +++++---- ietf/templates/doc/document_draft.html | 2 +- ietf/templates/doc/draft/change_stream_state.html | 3 +-- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 5e7e9a3d7..5d4c873ea 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -1030,7 +1030,7 @@ class ChangeStreamStateTests(django.test.TestCase): draft.tags = DocTagName.objects.filter(slug="w-expert") draft.group.unused_tags.add("w-refdoc") - url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name)) + url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name, state_type="draft-stream-ietf")) login_testing_unauthorized(self, "marschairman", url) # get @@ -1068,7 +1068,7 @@ class ChangeStreamStateTests(django.test.TestCase): def test_set_state(self): draft = make_test_data() - url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name)) + url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name, state_type="draft-stream-ietf")) login_testing_unauthorized(self, "marschairman", url) # get diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index 6453de180..6d46b3017 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -87,7 +87,7 @@ urlpatterns += patterns('', url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherdwriteup/$', views_draft.edit_shepherd_writeup, name='doc_edit_shepherd_writeup'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/requestpublication/$', views_draft.request_publication, name='doc_request_publication'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/adopt/$', views_draft.adopt_draft, name='doc_adopt_draft'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/stream/$', views_draft.change_stream_state, name='doc_change_stream_state'), + url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/(?Pdraft-stream-[a-z]+)/$', views_draft.change_stream_state, name='doc_change_stream_state'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/clearballot/$', views_ballot.clear_ballot, name='doc_clear_ballot'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/deferballot/$', views_ballot.defer_ballot, name='doc_defer_ballot'), diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index fe5a42214..180a478ff 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -258,9 +258,11 @@ def document_main(request, name, rev=None): resurrected_by = e.by # stream info + stream_state_type_slug = None stream_state = None if doc.stream: - stream_state = doc.get_state("draft-stream-%s" % doc.stream_id) + stream_state_type_slug = "draft-stream-%s" % doc.stream_id + stream_state = doc.get_state(stream_state_type_slug) stream_tags = doc.tags.filter(slug__in=get_tags_for_stream_id(doc.stream_id)) shepherd_writeup = doc.latest_event(WriteupDocEvent, type="changed_protocol_writeup") @@ -356,6 +358,7 @@ def document_main(request, name, rev=None): has_errata=doc.tags.filter(slug="errata"), published=doc.latest_event(type="published_rfc"), file_urls=file_urls, + stream_state_type_slug=stream_state_type_slug, stream_state=stream_state, stream_tags=stream_tags, milestones=doc.groupmilestone_set.filter(state="active"), diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index 7386750e2..5f5b864eb 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -1198,7 +1198,7 @@ def adopt_draft(request, name): context_instance=RequestContext(request)) class ChangeStreamStateForm(forms.Form): - new_state = forms.ModelChoiceField(queryset=State.objects.filter(used=True), label='State') + new_state = forms.ModelChoiceField(queryset=State.objects.filter(used=True), label='State', help_text=u"Only select 'Submitted to IESG for Publication' to correct errors. Use the document's main page to request publication.") weeks = forms.IntegerField(label='Expected weeks in state',required=False) comment = forms.CharField(widget=forms.Textarea, required=False, help_text="Optional comment for the document history") tags = forms.ModelMultipleChoiceField(queryset=DocTagName.objects.filter(used=True), widget=forms.CheckboxSelectMultiple, required=False) @@ -1244,15 +1244,16 @@ def next_states_for_stream_state(doc, state_type, current_state): return next_states @login_required -def change_stream_state(request, name): +def change_stream_state(request, name, state_type): doc = get_object_or_404(Document, type="draft", name=name) if not doc.stream: raise Http404 + state_type = get_object_or_404(StateType, slug=state_type) + if not is_authorized_in_doc_stream(request.user, doc): return HttpResponseForbidden("You don't have permission to access this page") - state_type = StateType.objects.get(slug="draft-stream-%s" % doc.stream_id) prev_state = doc.get_state(state_type.slug) next_states = next_states_for_stream_state(doc, state_type, prev_state) @@ -1308,7 +1309,7 @@ def change_stream_state(request, name): return HttpResponseRedirect(doc.get_absolute_url()) else: - form = ChangeStreamStateForm(initial=dict(new_state=prev_state.pk), + form = ChangeStreamStateForm(initial=dict(new_state=prev_state.pk if prev_state else None), doc=doc, state_type=state_type) milestones = doc.groupmilestone_set.all() diff --git a/ietf/templates/doc/document_draft.html b/ietf/templates/doc/document_draft.html index d5eb3bd1a..9b1883084 100644 --- a/ietf/templates/doc/document_draft.html +++ b/ietf/templates/doc/document_draft.html @@ -95,7 +95,7 @@ {% if doc.stream %} {{ doc.stream }} State: - + {{ stream_state|default:"(None)" }} diff --git a/ietf/templates/doc/draft/change_stream_state.html b/ietf/templates/doc/draft/change_stream_state.html index 172a20b8e..1676aa1aa 100644 --- a/ietf/templates/doc/draft/change_stream_state.html +++ b/ietf/templates/doc/draft/change_stream_state.html @@ -32,7 +32,7 @@ form.change-state ul li label { cursor: pointer; } {% if next_states %}
      - Recommended next state{{next_states|pluralize}}: + Recommended next state{{ next_states|pluralize }}: {% for state in next_states %}{{ state.name }} {% if not forloop.last %} or {% endif %}{% endfor %}
      {% endif %} @@ -65,7 +65,6 @@ jQuery(document).ready(function () { jQuery(".next-states a").click(function (e) { e.preventDefault(); var s = jQuery(this).data("state"); - console.log(s) jQuery("#id_new_state").val(s); }); }); From 8145d688de9263d673a505790a7747aa8af6b03e Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sat, 28 Sep 2013 18:57:59 +0000 Subject: [PATCH 041/173] Remove references to ietfworkflows/urls.py - Legacy-Id: 6296 --- ietf/group/stream_urls.py | 1 - ietf/urls.py | 1 - 2 files changed, 2 deletions(-) diff --git a/ietf/group/stream_urls.py b/ietf/group/stream_urls.py index 05f7731a9..39fbb7176 100644 --- a/ietf/group/stream_urls.py +++ b/ietf/group/stream_urls.py @@ -8,5 +8,4 @@ urlpatterns = patterns('', (r'^$', views_stream.streams), (r'^(?P[a-zA-Z0-9-]+)/$', views_stream.stream_documents, None), (r'^(?P[a-zA-Z0-9-]+)/edit/$', views_stream.stream_edit), - (r'^management/', include('ietf.ietfworkflows.urls')), ) diff --git a/ietf/urls.py b/ietf/urls.py index bf635ed21..9f9902345 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -62,7 +62,6 @@ urlpatterns = patterns('', (r'^secr/', include('ietf.secr.urls')), (r'^sitemap-(?P
      .+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}), (r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', { 'sitemaps': sitemaps}), - (r'^streams/', include('ietf.ietfworkflows.urls')), (r'^submit/', include('ietf.submit.urls')), (r'^sync/', include('ietf.sync.urls')), (r'^wg/', include('ietf.wginfo.urls')), From 050929a56d798fe5523fca2a836e7d442a51ea45 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sat, 28 Sep 2013 19:48:48 +0000 Subject: [PATCH 042/173] Remove ietfworkflows which is now obsolete - Legacy-Id: 6297 --- ietf/doc/views_draft.py | 4 +- ietf/ietfworkflows/.gitignore | 1 - ietf/ietfworkflows/__init__.py | 18 - ietf/ietfworkflows/accounts.py | 132 ---- ietf/ietfworkflows/constants.py | 13 - ietf/ietfworkflows/fixtures/.gitignore | 1 - ietf/ietfworkflows/fixtures/initial_data.xml | 686 ------------------ ietf/ietfworkflows/forms.py | 350 --------- ietf/ietfworkflows/migrations/.gitignore | 1 - ietf/ietfworkflows/migrations/0001_initial.py | 148 ---- .../0002_add_selected_states_and_tags.py | 107 --- .../migrations/0003_add_person_to_history.py | 119 --- .../migrations/0004_add_object_state_dates.py | 132 ---- .../migrations/0005_add_streams.py | 209 ------ .../0006_add_group_to_streamed_id.py | 198 ----- .../migrations/0007_do_stream_optional.py | 194 ----- .../0008_refactor_history_entries.py | 267 ------- .../0009_allow_null_in_from_date.py | 197 ----- .../migrations/0010_add_state_definitions.py | 207 ------ .../0011_remove_group_from_stream.py | 205 ------ .../0012_refactor_stream_group_descrition.py | 222 ------ .../migrations/0013_add_stream_delegates.py | 208 ------ .../0014_change_alt_streams_states.py | 234 ------ ietf/ietfworkflows/migrations/__init__.py | 0 ietf/ietfworkflows/models.py | 268 ------- ietf/ietfworkflows/streams.py | 102 --- ietf/ietfworkflows/templatetags/.gitignore | 1 - ietf/ietfworkflows/templatetags/__init__.py | 0 .../templatetags/ietf_streams.py | 112 --- .../templatetags/ietf_streams_redesign.py | 102 --- ietf/ietfworkflows/tests.py | 188 ----- ietf/ietfworkflows/urls.py | 13 - ietf/ietfworkflows/utils.py | 466 ------------ ietf/ietfworkflows/views.py | 150 ---- ietf/settings.py | 1 - .../annotation_tags_updated_mail.txt | 11 - .../templates/ietfworkflows/edit_actions.html | 6 - .../ietfworkflows/noworkflow_state_form.html | 23 - ietf/templates/ietfworkflows/state_edit.html | 51 -- ietf/templates/ietfworkflows/state_form.html | 32 - .../ietfworkflows/state_updated_mail.txt | 11 - .../ietfworkflows/stream_delegates.html | 43 -- ietf/templates/ietfworkflows/stream_form.html | 22 - .../ietfworkflows/stream_history.html | 44 -- .../templates/ietfworkflows/stream_state.html | 11 - .../ietfworkflows/stream_state_redesign.html | 10 - .../ietfworkflows/stream_updated_mail.txt | 11 - ietf/templates/ietfworkflows/tags_form.html | 22 - .../ietfworkflows/workflow_history_entry.html | 12 - 49 files changed, 1 insertion(+), 5564 deletions(-) delete mode 100644 ietf/ietfworkflows/.gitignore delete mode 100644 ietf/ietfworkflows/__init__.py delete mode 100644 ietf/ietfworkflows/accounts.py delete mode 100644 ietf/ietfworkflows/constants.py delete mode 100644 ietf/ietfworkflows/fixtures/.gitignore delete mode 100644 ietf/ietfworkflows/fixtures/initial_data.xml delete mode 100644 ietf/ietfworkflows/forms.py delete mode 100644 ietf/ietfworkflows/migrations/.gitignore delete mode 100644 ietf/ietfworkflows/migrations/0001_initial.py delete mode 100644 ietf/ietfworkflows/migrations/0002_add_selected_states_and_tags.py delete mode 100644 ietf/ietfworkflows/migrations/0003_add_person_to_history.py delete mode 100644 ietf/ietfworkflows/migrations/0004_add_object_state_dates.py delete mode 100644 ietf/ietfworkflows/migrations/0005_add_streams.py delete mode 100644 ietf/ietfworkflows/migrations/0006_add_group_to_streamed_id.py delete mode 100644 ietf/ietfworkflows/migrations/0007_do_stream_optional.py delete mode 100644 ietf/ietfworkflows/migrations/0008_refactor_history_entries.py delete mode 100644 ietf/ietfworkflows/migrations/0009_allow_null_in_from_date.py delete mode 100644 ietf/ietfworkflows/migrations/0010_add_state_definitions.py delete mode 100644 ietf/ietfworkflows/migrations/0011_remove_group_from_stream.py delete mode 100644 ietf/ietfworkflows/migrations/0012_refactor_stream_group_descrition.py delete mode 100644 ietf/ietfworkflows/migrations/0013_add_stream_delegates.py delete mode 100644 ietf/ietfworkflows/migrations/0014_change_alt_streams_states.py delete mode 100644 ietf/ietfworkflows/migrations/__init__.py delete mode 100644 ietf/ietfworkflows/models.py delete mode 100644 ietf/ietfworkflows/streams.py delete mode 100644 ietf/ietfworkflows/templatetags/.gitignore delete mode 100644 ietf/ietfworkflows/templatetags/__init__.py delete mode 100644 ietf/ietfworkflows/templatetags/ietf_streams.py delete mode 100644 ietf/ietfworkflows/templatetags/ietf_streams_redesign.py delete mode 100644 ietf/ietfworkflows/tests.py delete mode 100644 ietf/ietfworkflows/urls.py delete mode 100644 ietf/ietfworkflows/utils.py delete mode 100644 ietf/ietfworkflows/views.py delete mode 100644 ietf/templates/ietfworkflows/annotation_tags_updated_mail.txt delete mode 100644 ietf/templates/ietfworkflows/edit_actions.html delete mode 100644 ietf/templates/ietfworkflows/noworkflow_state_form.html delete mode 100644 ietf/templates/ietfworkflows/state_edit.html delete mode 100644 ietf/templates/ietfworkflows/state_form.html delete mode 100644 ietf/templates/ietfworkflows/state_updated_mail.txt delete mode 100644 ietf/templates/ietfworkflows/stream_delegates.html delete mode 100644 ietf/templates/ietfworkflows/stream_form.html delete mode 100644 ietf/templates/ietfworkflows/stream_history.html delete mode 100644 ietf/templates/ietfworkflows/stream_state.html delete mode 100644 ietf/templates/ietfworkflows/stream_state_redesign.html delete mode 100644 ietf/templates/ietfworkflows/stream_updated_mail.txt delete mode 100644 ietf/templates/ietfworkflows/tags_form.html delete mode 100644 ietf/templates/ietfworkflows/workflow_history_entry.html diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index 5f5b864eb..b225b9e0b 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -25,8 +25,6 @@ from ietf.utils.textupload import get_cleaned_text_file_content from ietf.person.forms import EmailsField from ietf.group.models import Group -from ietf.ietfworkflows.accounts import can_edit_state - from ietf.doc.models import * from ietf.doc.utils import * from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName @@ -1026,7 +1024,7 @@ def request_publication(request, name): doc = get_object_or_404(Document, type="draft", name=name, stream__in=("iab", "ise", "irtf")) - if not can_edit_state(request.user, doc): + if not is_authorized_in_doc_stream(request.user, doc): return HttpResponseForbidden("You do not have the necessary permissions to view this page") m = Message() diff --git a/ietf/ietfworkflows/.gitignore b/ietf/ietfworkflows/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/__init__.py b/ietf/ietfworkflows/__init__.py deleted file mode 100644 index e8d53c9a3..000000000 --- a/ietf/ietfworkflows/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# coding: latin-1 - -from types import ModuleType -import urls, models, views, forms, accounts - -# These people will be sent a stack trace if there's an uncaught exception in -# code any of the modules imported above: -DEBUG_EMAILS = [ - ('Emilio A. Sánchez', 'esanchez@yaco.es'), -] - -for k in locals().keys(): - m = locals()[k] - if isinstance(m, ModuleType): - if hasattr(m, "DEBUG_EMAILS"): - DEBUG_EMAILS += list(getattr(m, "DEBUG_EMAILS")) - setattr(m, "DEBUG_EMAILS", DEBUG_EMAILS) - diff --git a/ietf/ietfworkflows/accounts.py b/ietf/ietfworkflows/accounts.py deleted file mode 100644 index 5928628c6..000000000 --- a/ietf/ietfworkflows/accounts.py +++ /dev/null @@ -1,132 +0,0 @@ -from django.conf import settings - -from django.db.models import Q - -from ietf.ietfworkflows.streams import get_streamed_draft -from ietf.group.models import Role - - -def get_person_for_user(user): - try: - return user.get_profile().person() - except: - return None - - -def is_secretariat(user): - if not user or not user.is_authenticated(): - return False - return bool(user.groups.filter(name='Secretariat')) - - -def is_wgchair(person): - return bool(person.wgchair_set.all()) - -def is_wgchairREDESIGN(person): - return bool(Role.objects.filter(name="chair", group__type="wg", group__state="active", person=person)) - -def is_rgchairREDESIGN(person): - return bool(Role.objects.filter(name="chair", group__type="rg", group__state="active", person=person)) - -def is_wgdelegate(person): - return bool(person.wgdelegate_set.all()) - -def is_wgdelegateREDESIGN(person): - return bool(Role.objects.filter(name="delegate", group__type="wg", group__state="active", person=person)) - -def is_rgdelegateREDESIGN(person): - return bool(Role.objects.filter(name="delegate", group__type="rg", group__state="active", person=person)) - -def is_delegate_of_stream(user, stream): - if is_secretariat(user): - return True - person = get_person_for_user(user) - return stream.check_delegate(person) - -def is_delegate_of_streamREDESIGN(user, stream): - if is_secretariat(user): - return True - return user.is_authenticated() and bool(Role.objects.filter(group__acronym=stream.slug, name="delegate", person__user=user)) - - -def is_chair_of_stream(user, stream): - if is_secretariat(user): - return True - person = get_person_for_user(user) - return stream.check_chair(person) - -def is_chair_of_streamREDESIGN(user, stream): - if is_secretariat(user): - return True - if isinstance(user, basestring): - return False - return user.is_authenticated() and bool(Role.objects.filter(group__acronym=stream.slug, name="chair", person__user=user)) - -def is_authorized_in_draft_stream(user, draft): - if is_secretariat(user): - return True - person = get_person_for_user(user) - if not person: - return False - streamed = get_streamed_draft(draft) - if not streamed or not streamed.stream: - return False - # Check if the person is chair of the stream - if is_chair_of_stream(user, streamed.stream): - return True - # Check if the person is delegate of the stream - if is_delegate_of_stream(user, streamed.stream): - return True - # Check if the person is chair of the related group - chairs = streamed.stream.get_chairs_for_document(draft) - if chairs and person in [i.person for i in chairs]: - return True - # Check if the person is authorized by a delegate system - delegates = streamed.stream.get_delegates_for_document(draft) - return bool(person in delegates) - -def is_authorized_in_draft_streamREDESIGN(user, draft): - if is_secretariat(user): - return True - - from ietf.doc.models import Document - - if not super(Document, draft).stream: - return False - - # must be a chair or delegate of the stream group (or draft group) - group_req = Q(group__acronym=super(Document, draft).stream.slug) - if draft.group and super(Document, draft).stream.slug in ["ietf", "irtf"]: - group_req |= Q(group=draft.group) - - return user.is_authenticated() and bool(Role.objects.filter(name__in=("chair", "secr", "delegate"), person__user=user).filter(group_req)) - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.accounts import is_secretariat, get_person_for_user - is_wgdelegate = is_wgdelegateREDESIGN - is_wgchair = is_wgchairREDESIGN - is_rgdelegate = is_rgdelegateREDESIGN - is_rgchair = is_rgchairREDESIGN - is_chair_of_stream = is_chair_of_streamREDESIGN - is_delegate_of_stream = is_delegate_of_streamREDESIGN - is_authorized_in_draft_stream = is_authorized_in_draft_streamREDESIGN - - -def can_edit_state(user, draft): - return (is_secretariat(user) or - is_authorized_in_draft_stream(user, draft)) - - -def can_edit_stream(user, draft): - return is_secretariat(user) - -def can_adopt(user, draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES and (not draft.stream_id or draft.stream_id in ["ietf", "irtf"]) and draft.group.type_id == "individ": - person = get_person_for_user(user) - if not person: - return False - return is_wgchair(person) or is_rgchair(person) or is_wgdelegate(person) or is_rgdelegate(person) or is_secretariat(user) - else: - return is_secretariat(user) - diff --git a/ietf/ietfworkflows/constants.py b/ietf/ietfworkflows/constants.py deleted file mode 100644 index d29a1164f..000000000 --- a/ietf/ietfworkflows/constants.py +++ /dev/null @@ -1,13 +0,0 @@ -# Required states -CALL_FOR_ADOPTION = 'Call For Adoption By WG Issued' -WG_DOCUMENT = 'WG Document' -SUBMITTED_TO_IESG = 'Submitted to IESG for Publication' - -REQUIRED_STATES = ( - CALL_FOR_ADOPTION, - WG_DOCUMENT, - SUBMITTED_TO_IESG, - ) - -# IETF Stream -IETF_STREAM = 'IETF' diff --git a/ietf/ietfworkflows/fixtures/.gitignore b/ietf/ietfworkflows/fixtures/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/fixtures/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/fixtures/initial_data.xml b/ietf/ietfworkflows/fixtures/initial_data.xml deleted file mode 100644 index 51e06c12f..000000000 --- a/ietf/ietfworkflows/fixtures/initial_data.xml +++ /dev/null @@ -1,686 +0,0 @@ - - - - Default WG Workflow - 11 - - - IAB Workflow - - - - IRTF Workflow - - - - ISE Workflow - - - - Active IAB Document - 2 - - - - Active RG Document - 3 - - - - Adopted by a WG - 1 - - - - Adopted for WG Info Only - 1 - - - - Approved by IAB, To Be Sent to RFC Editor - 2 - - - - Awaiting IRSG Reviews - 3 - - - - Call For Adoption By WG Issued - 1 - - - - Candidate IAB Document - 2 - - - - Candidate RG Document - 3 - - - - Community Review - 2 - - - - Dead IAB Document - 2 - - - - Dead IRTF Document - 3 - - - - Dead WG Document - 1 - - - - Document on Hold Based On IESG Request - 3 - - - - Document on Hold Based On IESG Request - 4 - - - - Finding Reviewers - 4 - - - - IAB Review - 2 - - - - In IESG Review - 3 - - - - In IESG Review - 4 - - - - In IRSG Poll - 3 - - - - In ISE Review - 4 - - - - In RG Last Call - 3 - - - - In WG Last Call - 1 - - - - No Longer In Independent Submission Stream - 4 - - - - Parked IAB Document - 2 - - - - Parked RG Document - 3 - - - - Parked WG Document - 1 - - - - Response to Review Needed - 4 - - - - Sent to a Different Organization for Publication - 2 - - - - Sent to the RFC Editor - 2 - - - - Sent to the RFC Editor - 4 - - - - Sent to the RFC Editor - 3 - - - - Submission Received - 4 - - - - Submitted to IESG for Publication - 1 - - - - Waiting for Document Shepherd - 3 - - - - Waiting for IRTF Chair - 3 - - - - Waiting for WG Chair Go-Ahead - 1 - - - - WG Consensus: Waiting for Write-Up - 1 - - - - WG Document - 1 - - - - Wait for go-ahead - 1 - 18 - - - - - Reach consensus - 1 - 19 - - - - - Adopt - 1 - 12 - - - - - Adopt for WG info only - 1 - 13 - - - - - Develop - 1 - 14 - - - - - Park - 1 - 15 - - - - - Die - 1 - 16 - - - - - Submit to IESG - 1 - 20 - - - - - Raise last call - 1 - 17 - - - - - Revised I-D Needed - 3 - - - - Shepherd Needed - 3 - - - - Waiting for Dependency on Other Document - 3 - - - - Revised I-D Needed - 2 - - - - Document Shepherd Followup - 2 - - - - Editor Needed - 3 - - - - Awaiting Reviews - 2 - - - - Waiting for Partner Feedback - 2 - - - - Other - see Comment Log - 1 - - - - Editor Needed - 2 - - - - Doc Shepherd Follow-Up Underway - 1 - - - - Revised I-D Needed - Issue raised by IESG - 1 - - - - Awaiting Expert Review/Resolution of Issues Raised - 1 - - - - Awaiting External Review/Resolution of Issues Raised - 1 - - - - Awaiting Merge with Other Document - 1 - - - - Author or Editor Needed - 1 - - - - Waiting for Referenced Document - 1 - - - - Revised I-D Needed - Issue raised by AD - 1 - - - - Revised I-D Needed - Issue raised by WGLC - 1 - - - - Waiting for Referencing Document - 1 - - - - IESG Review Completed - 3 - - - - Waiting for Dependency on Other Document - 4 - - - - Awaiting Reviews - 4 - - - - Revised I-D Needed - 4 - - - - IESG Review Completed - 4 - - - - - - - - - - - - - - - - - - - - IETF - group.ietfwg - chairs - 1 - - - IAB - - - 2 - - - IRTF - - - 3 - - - ISE - - - 4 - - - 11 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.1" target="_blank">4.2.1. Call for Adoption by WG Issued</a> - - - The "Call for Adoption by WG Issued" state should be used to indicate - when an I-D is being considered for adoption by an IETF WG. An I-D - that is in this state is actively being considered for adoption and - has not yet achieved consensus, preference, or selection in the WG. - - This state may be used to describe an I-D that someone has asked a WG - to consider for adoption, if the WG Chair has agreed with the - request. This state may also be used to identify an I-D that a WG - Chair asked an author to write specifically for consideration as a - candidate WG item [WGDTSPEC], and/or an I-D that is listed as a - 'candidate draft' in the WG's charter. - - Under normal conditions, it should not be possible for an I-D to be - in the "Call for Adoption by WG Issued" state in more than one - working group at the same time. This said, it is not uncommon for - authors to "shop" their I-Ds to more than one WG at a time, with the - hope of getting their documents adopted somewhere. - - After this state is implemented in the Datatracker, an I-D that is in - the "Call for Adoption by WG Issued" state will not be able to be - "shopped" to any other WG without the consent of the WG Chairs and - the responsible ADs impacted by the shopping. - - Note that Figure 1 includes an arc leading from this state to outside - of the WG state machine. This illustrates that some I-Ds that are - considered do not get adopted as WG drafts. An I-D that is not - adopted as a WG draft will transition out of the WG state machine and - revert back to having no stream-specific state; however, the status - change history log of the I-D will record that the I-D was previously - in the "Call for Adoption by WG Issued" state. - - 1 - - - 12 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.2" target="_blank">4.2.2. Adopted by a WG</a> - - - The "Adopted by a WG" state describes an individual submission I-D - that an IETF WG has agreed to adopt as one of its WG drafts. - - WG Chairs who use this state will be able to clearly indicate when - their WGs adopt individual submission I-Ds. This will facilitate the - Datatracker's ability to correctly capture "Replaces" information for - WG drafts and correct "Replaced by" information for individual - submission I-Ds that have been replaced by WG drafts. - - This state is needed because the Datatracker uses the filename of an - I-D as a key to search its database for status information about the - I-D, and because the filename of a WG I-D is supposed to be different - from the filename of an individual submission I-D. - The filename of an individual submission I-D will typically be - formatted as 'draft-author-wgname-topic-nn'. - - The filename of a WG document is supposed to be formatted as 'draft- - ietf-wgname-topic-nn'. - - An individual I-D that is adopted by a WG may take weeks or months to - be resubmitted by the author as a new (version-00) WG draft. If the - "Adopted by a WG" state is not used, the Datatracker has no way to - determine that an I-D has been adopted until a new version of the I-D - is submitted to the WG by the author and until the I-D is approved - for posting by a WG Chair. - - 2 - - - 13 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.3" target="_blank">4.2.3. Adopted for WG Info Only</a> - - - The "Adopted for WG Info Only" state describes a document that - contains useful information for the WG that adopted it, but the - document is not intended to be published as an RFC. The WG will not - actively develop the contents of the I-D or progress it for - publication as an RFC. The only purpose of the I-D is to provide - information for internal use by the WG. - - 3 - - - 14 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.4" target="_blank">4.2.4. WG Document</a> - - - The "WG Document" state describes an I-D that has been adopted by an - IETF WG and is being actively developed. - - A WG Chair may transition an I-D into the "WG Document" state at any - time as long as the I-D is not being considered or developed in any - other WG. - - Alternatively, WG Chairs may rely upon new functionality to be added - to the Datatracker to automatically move version-00 drafts into the - "WG Document" state as described in Section 4.1. - - Under normal conditions, it should not be possible for an I-D to be - in the "WG Document" state in more than one WG at a time. This said, - I-Ds may be transferred from one WG to another with the consent of - the WG Chairs and the responsible ADs. - - - 4 - - - 15 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.5" target="_blank">4.2.5. Parked WG Document</a> - - - A "Parked WG Document" is an I-D that has lost its author or editor, - is waiting for another document to be written or for a review to be - completed, or cannot be progressed by the working group for some - other reason. - - Some of the annotation tags described in Section 4.3 may be used in - conjunction with this state to indicate why an I-D has been parked, - and/or what may need to happen for the I-D to be un-parked. - - Parking a WG draft will not prevent it from expiring; however, this - state can be used to indicate why the I-D has stopped progressing in - the WG. - - A "Parked WG Document" that is not expired may be transferred from - one WG to another with the consent of the WG Chairs and the - responsible ADs. - - - 5 - - - 16 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.6" target="_blank">4.2.6. Dead WG Document</a> - - - A "Dead WG Document" is an I-D that has been abandoned. Note that - 'Dead' is not always a final state for a WG I-D. If consensus is - subsequently achieved, a "Dead WG Document" may be resurrected. A - "Dead WG Document" that is not resurrected will eventually expire. - - Note that an I-D that is declared to be "Dead" in one WG and that is - not expired may be transferred to a non-dead state in another WG with - the consent of the WG Chairs and the responsible ADs. - - - 6 - - - 17 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.7" target="_blank">4.2.7. In WG Last Call</a> - - - A document "In WG Last Call" is an I-D for which a WG Last Call - (WGLC) has been issued and is in progress. - - Note that conducting a WGLC is an optional part of the IETF WG - process, per Section 7.4 of RFC 2418 [RFC2418]. - - If a WG Chair decides to conduct a WGLC on an I-D, the "In WG Last - Call" state can be used to track the progress of the WGLC. The Chair - may configure the Datatracker to send a WGLC message to one or more - mailing lists when the Chair moves the I-D into this state. The WG - Chair may also be able to select a different set of mailing lists for - a different document undergoing a WGLC; some documents may deserve - coordination with other WGs. - - A WG I-D in this state should remain "In WG Last Call" until the WG - Chair moves it to another state. The WG Chair may configure the - Datatracker to send an e-mail after a specified period of time to - remind or 'nudge' the Chair to conclude the WGLC and to determine the - next state for the document. - - It is possible for one WGLC to lead into another WGLC for the same - document. For example, an I-D that completed a WGLC as an - "Informational" document may need another WGLC if a decision is taken - to convert the I-D into a Standards Track document. - - - 7 - - - 18 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.8" target="_blank">4.2.8. Waiting for WG Chair Go-Ahead</a> - - - A WG Chair may wish to place an I-D that receives a lot of comments - during a WGLC into the "Waiting for WG Chair Go-Ahead" state. This - state describes an I-D that has undergone a WGLC; however, the Chair - is not yet ready to call consensus on the document. - - If comments from the WGLC need to be responded to, or a revision to - the I-D is needed, the Chair may place an I-D into this state until - all of the WGLC comments are adequately addressed and the (possibly - revised) document is in the I-D repository. - - - 8 - - - 19 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.9" target="_blank">4.2.9. WG Consensus: Waiting for Writeup</a> - - - A document in the "WG Consensus: Waiting for Writeup" state has - essentially completed its development within the working group, and - is nearly ready to be sent to the IESG for publication. The last - thing to be done is the preparation of a protocol writeup by a - Document Shepherd. The IESG requires that a document shepherd - writeup be completed before publication of the I-D is requested. The - IETF document shepherding process and the role of a WG Document - Shepherd is described in RFC 4858 [RFC4858] - - A WG Chair may call consensus on an I-D without a formal WGLC and - transition an I-D that was in the "WG Document" state directly into - this state. - - The name of this state includes the words "Waiting for Writeup" - because a good document shepherd writeup takes time to prepare. - - - 9 - - - 20 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.10" target="_blank">4.2.10. Submitted to IESG for Publication</a> - - - This state describes a WG document that has been submitted to the - IESG for publication and that has not been sent back to the working - group for revision. - - An I-D in this state may be under review by the IESG, it may have - been approved and be in the RFC Editor's queue, or it may have been - published as an RFC. Other possibilities exist too. The document - may be "Dead" (in the IESG state machine) or in a "Do Not Publish" - state. - - - 10 - - diff --git a/ietf/ietfworkflows/forms.py b/ietf/ietfworkflows/forms.py deleted file mode 100644 index fc7e9a2c4..000000000 --- a/ietf/ietfworkflows/forms.py +++ /dev/null @@ -1,350 +0,0 @@ -import datetime - -from django.conf import settings -from django import forms -from django.template.loader import render_to_string -from workflows.models import State -from workflows.utils import set_workflow_for_object - -from ietf.idtracker.models import PersonOrOrgInfo, IETFWG, InternetDraft -from ietf.ietfworkflows.models import Stream, StreamDelegate -from ietf.ietfworkflows.utils import (get_workflow_for_draft, get_workflow_for_wg, - get_state_for_draft, get_state_by_name, - update_state, FOLLOWUP_TAG, - get_annotation_tags_for_draft, - update_tags, update_stream) -from ietf.ietfworkflows.accounts import is_secretariat -from ietf.ietfworkflows.streams import (get_stream_from_draft, get_streamed_draft, - get_stream_by_name, set_stream_for_draft) -from ietf.ietfworkflows.constants import CALL_FOR_ADOPTION, IETF_STREAM -from ietf.doc.utils import get_tags_for_stream_id -from ietf.doc.models import save_document_in_history, DocEvent, Document -from ietf.name.models import DocTagName, StreamName, RoleName -from ietf.group.models import Group, GroupStateTransitions, Role -from ietf.group.utils import save_group_in_history -from ietf.person.models import Person, Email - -class StreamDraftForm(forms.Form): - - can_cancel = False - template = None - - def __init__(self, *args, **kwargs): - self.draft = kwargs.pop('draft', None) - self.user = kwargs.pop('user', None) - try: - self.person = self.user.get_profile() - except: - self.person = None - self.workflow = get_workflow_for_draft(self.draft) - self.message = {} - super(StreamDraftForm, self).__init__(*args, **kwargs) - - def get_message(self): - return self.message - - def set_message(self, msg_type, msg_value): - self.message = {'type': msg_type, - 'value': msg_value, - } - - def __unicode__(self): - return render_to_string(self.template, {'form': self}) - - -class NoWorkflowStateForm(StreamDraftForm): - comment = forms.CharField(widget=forms.Textarea, required=False) - weeks = forms.IntegerField(required=False) - group = forms.ChoiceField(required=False) - - template = 'ietfworkflows/noworkflow_state_form.html' - - def __init__(self, *args, **kwargs): - super(NoWorkflowStateForm, self).__init__(*args, **kwargs) - self.groups = None - if is_secretariat(self.user): - groups = IETFWG.objects.all().order_by('group_acronym__acronym') - else: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - groups = IETFWG.objects.filter(type__in=["wg", "rg"], state="active", role__name__in=("chair", "secr", "delegate"), role__person__user=self.user).order_by('acronym').distinct() - else: - groups = set([i.group_acronym for i in self.person.wgchair_set.all()]).union(set([i.wg for i in self.person.wgdelegate_set.all()])) - groups = list(groups) - groups.sort(lambda x, y: cmp(x.group_acronym.acronym, y.group_acronym.acronym)) - self.groups = groups - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - self.fields['group'].choices = [(i.pk, '%s - %s' % (i.acronym, i.name)) for i in self.groups] - else: - self.fields['group'].choices = [(i.pk, '%s - %s' % (i.group_acronym.acronym, i.group_acronym.name)) for i in self.groups] - - def save(self): - comment = self.cleaned_data.get('comment').strip() - weeks = self.cleaned_data.get('weeks') - group = IETFWG.objects.get(pk=self.cleaned_data.get('group')) - estimated_date = None - if weeks: - now = datetime.date.today() - estimated_date = now + datetime.timedelta(weeks=weeks) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # do changes on real Document object instead of proxy to avoid trouble - doc = Document.objects.get(pk=self.draft.pk) - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - - if group.type.slug == "rg": - new_stream = StreamName.objects.get(slug="irtf") - else: - new_stream = StreamName.objects.get(slug="ietf") - - if doc.stream != new_stream: - e = DocEvent(type="changed_stream") - e.time = doc.time - e.by = self.user.get_profile() - e.doc = doc - e.desc = u"Changed to %s" % new_stream.name - if doc.stream: - e.desc += u" from %s" % doc.stream.name - e.save() - doc.stream = new_stream - - if doc.group.pk != group.pk: - e = DocEvent(type="changed_group") - e.time = doc.time - e.by = self.user.get_profile() - e.doc = doc - e.desc = u"Changed group to %s (%s)" % (group.name, group.acronym.upper()) - if doc.group.type_id != "individ": - e.desc += " from %s (%s)" % (doc.group.name, doc.group.acronym) - e.save() - doc.group_id = group.pk - - doc.save() - self.draft = InternetDraft.objects.get(pk=doc.pk) # make sure proxy object is updated - else: - workflow = get_workflow_for_wg(wg) - set_workflow_for_object(self.draft, workflow) - stream = get_stream_by_name(IETF_STREAM) - streamed = get_streamed_draft(self.draft) - if not streamed: - set_stream_for_draft(self.draft, stream) - streamed = get_streamed_draft(self.draft) - streamed.stream = stream - streamed.group = wg - streamed.save() - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import State - if self.draft.stream_id == "irtf": - to_state = State.objects.get(used=True, slug="active", type="draft-stream-irtf") - else: - to_state = State.objects.get(used=True, slug="c-adopt", type="draft-stream-%s" % self.draft.stream_id) - else: - to_state = get_state_by_name(CALL_FOR_ADOPTION) - update_state(self.request, self.draft, - comment=comment, - person=self.person, - to_state=to_state, - estimated_date=estimated_date) - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if comment: - e = DocEvent(type="added_comment") - e.time = self.draft.time - e.by = self.person - e.doc_id = self.draft.pk - e.desc = comment - e.save() - -class DraftTagsStateForm(StreamDraftForm): - - new_state = forms.ChoiceField(label='State') - weeks = forms.IntegerField(label='Expected weeks in state',required=False) - comment = forms.CharField(widget=forms.Textarea, required=False) - tags = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False) - - template = 'ietfworkflows/state_form.html' - - def __init__(self, *args, **kwargs): - super(DraftTagsStateForm, self).__init__(*args, **kwargs) - self.state = get_state_for_draft(self.draft) - self.fields['new_state'].choices = self.get_states() - self.fields['new_state'].initial = self.state.pk if self.state else None - if self.draft.stream_id == 'ietf': - self.fields['new_state'].help_text = "Only select 'Submitted to IESG for Publication' to correct errors. Use the document's main page to request publication." - if self.is_bound: - for key, value in self.data.items(): - if key.startswith('transition_'): - new_state = self.get_new_state(key) - if new_state: - self.data = self.data.copy() - self.data.update({'new_state': new_state.id}) - if key.startswith('new_state_'): # hack to get value from submit buttons - self.data = self.data.copy() - self.data['new_state'] = key.replace('new_state_', '') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - possible_tags = get_tags_for_stream_id(self.draft.stream_id) - if self.draft.stream_id == "ietf" and self.draft.group: - unused_tags = self.draft.group.unused_tags.values_list("slug", flat=True) - possible_tags = [t for t in possible_tags if t not in unused_tags] - self.available_tags = DocTagName.objects.filter(slug__in=possible_tags) - self.tags = self.draft.tags.filter(slug__in=possible_tags) - else: - self.available_tags = self.workflow.get_tags() - self.tags = [i.annotation_tag for i in get_annotation_tags_for_draft(self.draft)] - - self.fields['tags'].choices = [(i.pk, i.name) for i in self.available_tags] - self.fields['tags'].initial = [i.pk for i in self.tags] - - def get_new_state(self, key): - transition_id = key.replace('transition_', '') - transition = self.get_transitions().filter(id=transition_id) - if transition: - return transition[0].destination - return None - - def get_transitions(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return [] - return self.state.transitions.filter(workflow=self.workflow) - - def get_next_states(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if not self.draft.stream_id: - return [] - - from ietf.doc.models import State - state_type = "draft-stream-%s" % self.draft.stream_id - s = self.draft.get_state(state_type) - next_states = [] - if s: - next_states = s.next_states.all() - - if self.draft.stream_id == "ietf" and self.draft.group: - transitions = self.draft.group.groupstatetransitions_set.filter(state=s) - if transitions: - next_states = transitions[0].next_states.all() - else: - # return the initial state - states = State.objects.filter(used=True, type=state_type).order_by('order') - if states: - next_states = states[:1] - - unused = [] - if self.draft.group: - unused = self.draft.group.unused_states.values_list("pk", flat=True) - return [n for n in next_states if n.pk not in unused] - - return [] - - - def get_states(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if not self.draft.stream_id: - return [] - - from ietf.doc.models import State - states = State.objects.filter(used=True, type="draft-stream-%s" % self.draft.stream_id) - if self.draft.stream_id == "ietf" and self.draft.group: - unused_states = self.draft.group.unused_states.values_list("pk", flat=True) - states = [s for s in states if s.pk not in unused_states] - return [(i.pk, i.name) for i in states] - - return [(i.pk, i.name) for i in self.workflow.get_states()] - - def save_tags(self): - comment = self.cleaned_data.get('comment') - new_tags = self.cleaned_data.get('tags') - - set_tags = [tag for tag in self.available_tags if str(tag.pk) in new_tags and tag not in self.tags] - reset_tags = [tag for tag in self.available_tags if str(tag.pk) not in new_tags and tag in self.tags] - followup = bool([tag for tag in set_tags if tag.name == FOLLOWUP_TAG]) - extra_notify = [] - if followup: - try: - shepherd = self.draft.shepherd - if shepherd: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - extra_notify = [shepherd.formatted_email()] - else: - extra_notify = ['%s <%s>' % shepherd.email()] - except PersonOrOrgInfo.DoesNotExist: - pass - if not set_tags and not reset_tags: - return - update_tags(self.request, self.draft, - comment=comment, - person=self.person, - set_tags=set_tags, - reset_tags=reset_tags, - extra_notify=extra_notify) - - def save_state(self): - comment = self.cleaned_data.get('comment') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import State - state = State.objects.get(pk=self.cleaned_data.get('new_state')) - weeks = self.cleaned_data.get('weeks') - estimated_date = None - if weeks: - now = datetime.date.today() - estimated_date = now + datetime.timedelta(weeks=weeks) - - update_state(self.request, self.draft, - comment=comment, - person=self.person, - to_state=state, - estimated_date=estimated_date) - - def save(self): - self.save_tags() - if 'only_tags' not in self.data.keys(): - self.save_state() - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - comment = self.cleaned_data.get('comment').strip() - if comment: - e = DocEvent(type="added_comment") - e.time = datetime.datetime.now() - e.by = self.person - e.doc_id = self.draft.pk - e.desc = comment - e.save() - - -class StreamDelegatesForm(forms.Form): - email = forms.EmailField() - - def __init__(self, *args, **kwargs): - self.stream = kwargs.pop('stream') - super(StreamDelegatesForm, self).__init__(*args, **kwargs) - - def get_person(self, email): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - persons = Person.objects.filter(email__address=email).distinct() - else: - persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).distinct() - if not persons: - return None - return persons[0] - - def clean_email(self): - email = self.cleaned_data.get('email') - self.person = self.get_person(email) - if not self.person: - raise forms.ValidationError('There is no user with this email in the system') - return email - - def save(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - stream_group = Group.objects.get(acronym=self.stream.slug) - save_group_in_history(stream_group) - Role.objects.get_or_create(person=self.person, - group=stream_group, - name=RoleName.objects.get(slug="delegate"), - email=Email.objects.get(address=self.cleaned_data.get('email'))) - return - - StreamDelegate.objects.get_or_create( - person=self.person, - stream=self.stream) diff --git a/ietf/ietfworkflows/migrations/.gitignore b/ietf/ietfworkflows/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/migrations/0001_initial.py b/ietf/ietfworkflows/migrations/0001_initial.py deleted file mode 100644 index 7d9f29e4f..000000000 --- a/ietf/ietfworkflows/migrations/0001_initial.py +++ /dev/null @@ -1,148 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'WGWorkflow' - db.create_table('ietfworkflows_wgworkflow', ( - ('workflow_ptr', orm['ietfworkflows.WGWorkflow:workflow_ptr']), - )) - db.send_create_signal('ietfworkflows', ['WGWorkflow']) - - # Adding model 'ObjectWorkflowHistoryEntry' - db.create_table('ietfworkflows_objectworkflowhistoryentry', ( - ('id', orm['ietfworkflows.ObjectWorkflowHistoryEntry:id']), - ('content_type', orm['ietfworkflows.ObjectWorkflowHistoryEntry:content_type']), - ('content_id', orm['ietfworkflows.ObjectWorkflowHistoryEntry:content_id']), - ('from_state', orm['ietfworkflows.ObjectWorkflowHistoryEntry:from_state']), - ('to_state', orm['ietfworkflows.ObjectWorkflowHistoryEntry:to_state']), - ('transition_date', orm['ietfworkflows.ObjectWorkflowHistoryEntry:transition_date']), - ('comment', orm['ietfworkflows.ObjectWorkflowHistoryEntry:comment']), - )) - db.send_create_signal('ietfworkflows', ['ObjectWorkflowHistoryEntry']) - - # Adding model 'ObjectAnnotationTagHistoryEntry' - db.create_table('ietfworkflows_objectannotationtaghistoryentry', ( - ('id', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:id']), - ('content_type', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:content_type']), - ('content_id', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:content_id']), - ('setted', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:setted']), - ('unsetted', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:unsetted']), - ('change_date', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:change_date']), - ('comment', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:comment']), - )) - db.send_create_signal('ietfworkflows', ['ObjectAnnotationTagHistoryEntry']) - - # Adding model 'AnnotationTag' - db.create_table('ietfworkflows_annotationtag', ( - ('id', orm['ietfworkflows.AnnotationTag:id']), - ('name', orm['ietfworkflows.AnnotationTag:name']), - ('workflow', orm['ietfworkflows.AnnotationTag:workflow']), - ('permission', orm['ietfworkflows.AnnotationTag:permission']), - )) - db.send_create_signal('ietfworkflows', ['AnnotationTag']) - - # Adding model 'AnnotationTagObjectRelation' - db.create_table('ietfworkflows_annotationtagobjectrelation', ( - ('id', orm['ietfworkflows.AnnotationTagObjectRelation:id']), - ('content_type', orm['ietfworkflows.AnnotationTagObjectRelation:content_type']), - ('content_id', orm['ietfworkflows.AnnotationTagObjectRelation:content_id']), - ('annotation_tag', orm['ietfworkflows.AnnotationTagObjectRelation:annotation_tag']), - )) - db.send_create_signal('ietfworkflows', ['AnnotationTagObjectRelation']) - - - - def backwards(self, orm): - - # Deleting model 'WGWorkflow' - db.delete_table('ietfworkflows_wgworkflow') - - # Deleting model 'ObjectWorkflowHistoryEntry' - db.delete_table('ietfworkflows_objectworkflowhistoryentry') - - # Deleting model 'ObjectAnnotationTagHistoryEntry' - db.delete_table('ietfworkflows_objectannotationtaghistoryentry') - - # Deleting model 'AnnotationTag' - db.delete_table('ietfworkflows_annotationtag') - - # Deleting model 'AnnotationTagObjectRelation' - db.delete_table('ietfworkflows_annotationtagobjectrelation') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.wgworkflow': { - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0002_add_selected_states_and_tags.py b/ietf/ietfworkflows/migrations/0002_add_selected_states_and_tags.py deleted file mode 100644 index 7545559b7..000000000 --- a/ietf/ietfworkflows/migrations/0002_add_selected_states_and_tags.py +++ /dev/null @@ -1,107 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding ManyToManyField 'WGWorkflow.selected_tags' - db.create_table('ietfworkflows_wgworkflow_selected_tags', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('wgworkflow', models.ForeignKey(orm.WGWorkflow, null=False)), - ('annotationtag', models.ForeignKey(orm.AnnotationTag, null=False)) - )) - - # Adding ManyToManyField 'WGWorkflow.selected_states' - db.create_table('ietfworkflows_wgworkflow_selected_states', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('wgworkflow', models.ForeignKey(orm.WGWorkflow, null=False)), - ('state', models.ForeignKey(orm['workflows.State'], null=False)) - )) - - - - def backwards(self, orm): - - # Dropping ManyToManyField 'WGWorkflow.selected_tags' - db.delete_table('ietfworkflows_wgworkflow_selected_tags') - - # Dropping ManyToManyField 'WGWorkflow.selected_states' - db.delete_table('ietfworkflows_wgworkflow_selected_states') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']"}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0003_add_person_to_history.py b/ietf/ietfworkflows/migrations/0003_add_person_to_history.py deleted file mode 100644 index 85f634656..000000000 --- a/ietf/ietfworkflows/migrations/0003_add_person_to_history.py +++ /dev/null @@ -1,119 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding field 'ObjectWorkflowHistoryEntry.person' - db.add_column('ietfworkflows_objectworkflowhistoryentry', 'person', orm['ietfworkflows.objectworkflowhistoryentry:person']) - - # Adding field 'ObjectAnnotationTagHistoryEntry.person' - db.add_column('ietfworkflows_objectannotationtaghistoryentry', 'person', orm['ietfworkflows.objectannotationtaghistoryentry:person']) - - - - def backwards(self, orm): - - # Deleting field 'ObjectWorkflowHistoryEntry.person' - db.delete_column('ietfworkflows_objectworkflowhistoryentry', 'person_id') - - # Deleting field 'ObjectAnnotationTagHistoryEntry.person' - db.delete_column('ietfworkflows_objectannotationtaghistoryentry', 'person_id') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'symmetrical': 'False'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'symmetrical': 'False'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0004_add_object_state_dates.py b/ietf/ietfworkflows/migrations/0004_add_object_state_dates.py deleted file mode 100644 index c71d2a08e..000000000 --- a/ietf/ietfworkflows/migrations/0004_add_object_state_dates.py +++ /dev/null @@ -1,132 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StateObjectRelationMetadata' - db.create_table('ietfworkflows_stateobjectrelationmetadata', ( - ('id', orm['ietfworkflows.stateobjectrelationmetadata:id']), - ('relation', orm['ietfworkflows.stateobjectrelationmetadata:relation']), - ('from_date', orm['ietfworkflows.stateobjectrelationmetadata:from_date']), - ('estimated_date', orm['ietfworkflows.stateobjectrelationmetadata:estimated_date']), - )) - db.send_create_signal('ietfworkflows', ['StateObjectRelationMetadata']) - - - - def backwards(self, orm): - - # Deleting model 'StateObjectRelationMetadata' - db.delete_table('ietfworkflows_stateobjectrelationmetadata') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'symmetrical': 'False'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'symmetrical': 'False'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0005_add_streams.py b/ietf/ietfworkflows/migrations/0005_add_streams.py deleted file mode 100644 index 1e3de388c..000000000 --- a/ietf/ietfworkflows/migrations/0005_add_streams.py +++ /dev/null @@ -1,209 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StreamedID' - db.create_table('ietfworkflows_streamedid', ( - ('id', orm['ietfworkflows.streamedid:id']), - ('draft', orm['ietfworkflows.streamedid:draft']), - ('stream', orm['ietfworkflows.streamedid:stream']), - )) - db.send_create_signal('ietfworkflows', ['StreamedID']) - - # Adding model 'Stream' - db.create_table('ietfworkflows_stream', ( - ('id', orm['ietfworkflows.stream:id']), - ('name', orm['ietfworkflows.stream:name']), - ('with_groups', orm['ietfworkflows.stream:with_groups']), - ('group_model', orm['ietfworkflows.stream:group_model']), - ('group_chair_model', orm['ietfworkflows.stream:group_chair_model']), - ('workflow', orm['ietfworkflows.stream:workflow']), - )) - db.send_create_signal('ietfworkflows', ['Stream']) - - - - def backwards(self, orm): - - # Deleting model 'StreamedID' - db.delete_table('ietfworkflows_streamedid') - - # Deleting model 'Stream' - db.delete_table('ietfworkflows_stream') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0006_add_group_to_streamed_id.py b/ietf/ietfworkflows/migrations/0006_add_group_to_streamed_id.py deleted file mode 100644 index 9460f0ac6..000000000 --- a/ietf/ietfworkflows/migrations/0006_add_group_to_streamed_id.py +++ /dev/null @@ -1,198 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding field 'StreamedID.content_type' - db.add_column('ietfworkflows_streamedid', 'content_type', orm['ietfworkflows.streamedid:content_type']) - - # Adding field 'StreamedID.content_id' - db.add_column('ietfworkflows_streamedid', 'content_id', orm['ietfworkflows.streamedid:content_id']) - - - - def backwards(self, orm): - - # Deleting field 'StreamedID.content_type' - db.delete_column('ietfworkflows_streamedid', 'content_type_id') - - # Deleting field 'StreamedID.content_id' - db.delete_column('ietfworkflows_streamedid', 'content_id') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0007_do_stream_optional.py b/ietf/ietfworkflows/migrations/0007_do_stream_optional.py deleted file mode 100644 index bcb758aab..000000000 --- a/ietf/ietfworkflows/migrations/0007_do_stream_optional.py +++ /dev/null @@ -1,194 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Changing field 'StreamedID.stream' - # (to signature: django.db.models.fields.related.ForeignKey(to=orm['ietfworkflows.Stream'], null=True, blank=True)) - db.alter_column('ietfworkflows_streamedid', 'stream_id', orm['ietfworkflows.streamedid:stream']) - - - - def backwards(self, orm): - - # Changing field 'StreamedID.stream' - # (to signature: django.db.models.fields.related.ForeignKey(to=orm['ietfworkflows.Stream'])) - db.alter_column('ietfworkflows_streamedid', 'stream_id', orm['ietfworkflows.streamedid:stream']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0008_refactor_history_entries.py b/ietf/ietfworkflows/migrations/0008_refactor_history_entries.py deleted file mode 100644 index 40bc6f36d..000000000 --- a/ietf/ietfworkflows/migrations/0008_refactor_history_entries.py +++ /dev/null @@ -1,267 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Deleting model 'objectworkflowhistoryentry' - db.delete_table('ietfworkflows_objectworkflowhistoryentry') - - # Deleting model 'objectannotationtaghistoryentry' - db.delete_table('ietfworkflows_objectannotationtaghistoryentry') - - # Adding model 'ObjectAnnotationTagHistoryEntry' - db.create_table('ietfworkflows_objectannotationtaghistoryentry', ( - ('objecthistoryentry_ptr', orm['ietfworkflows.objectannotationtaghistoryentry:objecthistoryentry_ptr']), - ('setted', orm['ietfworkflows.objectannotationtaghistoryentry:setted']), - ('unsetted', orm['ietfworkflows.objectannotationtaghistoryentry:unsetted']), - )) - db.send_create_signal('ietfworkflows', ['ObjectAnnotationTagHistoryEntry']) - - # Adding model 'ObjectHistoryEntry' - db.create_table('ietfworkflows_objecthistoryentry', ( - ('id', orm['ietfworkflows.objecthistoryentry:id']), - ('content_type', orm['ietfworkflows.objecthistoryentry:content_type']), - ('content_id', orm['ietfworkflows.objecthistoryentry:content_id']), - ('date', orm['ietfworkflows.objecthistoryentry:date']), - ('comment', orm['ietfworkflows.objecthistoryentry:comment']), - ('person', orm['ietfworkflows.objecthistoryentry:person']), - )) - db.send_create_signal('ietfworkflows', ['ObjectHistoryEntry']) - - # Adding model 'ObjectStreamHistoryEntry' - db.create_table('ietfworkflows_objectstreamhistoryentry', ( - ('objecthistoryentry_ptr', orm['ietfworkflows.objectstreamhistoryentry:objecthistoryentry_ptr']), - ('from_stream', orm['ietfworkflows.objectstreamhistoryentry:from_stream']), - ('to_stream', orm['ietfworkflows.objectstreamhistoryentry:to_stream']), - )) - db.send_create_signal('ietfworkflows', ['ObjectStreamHistoryEntry']) - - # Adding model 'ObjectWorkflowHistoryEntry' - db.create_table('ietfworkflows_objectworkflowhistoryentry', ( - ('objecthistoryentry_ptr', orm['ietfworkflows.objectworkflowhistoryentry:objecthistoryentry_ptr']), - ('from_state', orm['ietfworkflows.objectworkflowhistoryentry:from_state']), - ('to_state', orm['ietfworkflows.objectworkflowhistoryentry:to_state']), - )) - db.send_create_signal('ietfworkflows', ['ObjectWorkflowHistoryEntry']) - - - def backwards(self, orm): - - # Deleting model 'ObjectAnnotationTagHistoryEntry' - db.delete_table('ietfworkflows_objectannotationtaghistoryentry') - - # Deleting model 'ObjectHistoryEntry' - db.delete_table('ietfworkflows_objecthistoryentry') - - # Deleting model 'ObjectStreamHistoryEntry' - db.delete_table('ietfworkflows_objectstreamhistoryentry') - - # Deleting model 'ObjectWorkflowHistoryEntry' - db.delete_table('ietfworkflows_objectworkflowhistoryentry') - - # Adding model 'objectworkflowhistoryentry' - db.create_table('ietfworkflows_objectworkflowhistoryentry', ( - ('comment', orm['ietfworkflows.objectworkflowhistoryentry:comment']), - ('from_state', orm['ietfworkflows.objectworkflowhistoryentry:from_state']), - ('to_state', orm['ietfworkflows.objectworkflowhistoryentry:to_state']), - ('content_type', orm['ietfworkflows.objectworkflowhistoryentry:content_type']), - ('person', orm['ietfworkflows.objectworkflowhistoryentry:person']), - ('content_id', orm['ietfworkflows.objectworkflowhistoryentry:content_id']), - ('id', orm['ietfworkflows.objectworkflowhistoryentry:id']), - ('transition_date', orm['ietfworkflows.objectworkflowhistoryentry:transition_date']), - )) - db.send_create_signal('ietfworkflows', ['objectworkflowhistoryentry']) - - # Adding model 'objectannotationtaghistoryentry' - db.create_table('ietfworkflows_objectannotationtaghistoryentry', ( - ('comment', orm['ietfworkflows.objectannotationtaghistoryentry:comment']), - ('person', orm['ietfworkflows.objectannotationtaghistoryentry:person']), - ('unsetted', orm['ietfworkflows.objectannotationtaghistoryentry:unsetted']), - ('content_type', orm['ietfworkflows.objectannotationtaghistoryentry:content_type']), - ('change_date', orm['ietfworkflows.objectannotationtaghistoryentry:change_date']), - ('setted', orm['ietfworkflows.objectannotationtaghistoryentry:setted']), - ('content_id', orm['ietfworkflows.objectannotationtaghistoryentry:content_id']), - ('id', orm['ietfworkflows.objectannotationtaghistoryentry:id']), - )) - db.send_create_signal('ietfworkflows', ['objectannotationtaghistoryentry']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0009_allow_null_in_from_date.py b/ietf/ietfworkflows/migrations/0009_allow_null_in_from_date.py deleted file mode 100644 index b2c19c982..000000000 --- a/ietf/ietfworkflows/migrations/0009_allow_null_in_from_date.py +++ /dev/null @@ -1,197 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Changing field 'StateObjectRelationMetadata.from_date' - # (to signature: django.db.models.fields.DateTimeField(null=True, blank=True)) - db.alter_column('ietfworkflows_stateobjectrelationmetadata', 'from_date', orm['ietfworkflows.stateobjectrelationmetadata:from_date']) - - - - def backwards(self, orm): - - # Changing field 'StateObjectRelationMetadata.from_date' - # (to signature: django.db.models.fields.DateTimeField()) - db.alter_column('ietfworkflows_stateobjectrelationmetadata', 'from_date', orm['ietfworkflows.stateobjectrelationmetadata:from_date']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0010_add_state_definitions.py b/ietf/ietfworkflows/migrations/0010_add_state_definitions.py deleted file mode 100644 index 503943c56..000000000 --- a/ietf/ietfworkflows/migrations/0010_add_state_definitions.py +++ /dev/null @@ -1,207 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StateDescription' - db.create_table('ietfworkflows_statedescription', ( - ('id', orm['ietfworkflows.statedescription:id']), - ('state', orm['ietfworkflows.statedescription:state']), - ('definition', orm['ietfworkflows.statedescription:definition']), - ('order', orm['ietfworkflows.statedescription:order']), - )) - db.send_create_signal('ietfworkflows', ['StateDescription']) - - - - def backwards(self, orm): - - # Deleting model 'StateDescription' - db.delete_table('ietfworkflows_statedescription') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0011_remove_group_from_stream.py b/ietf/ietfworkflows/migrations/0011_remove_group_from_stream.py deleted file mode 100644 index 5705f7ef8..000000000 --- a/ietf/ietfworkflows/migrations/0011_remove_group_from_stream.py +++ /dev/null @@ -1,205 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Deleting field 'StreamedID.content_type' - db.delete_column('ietfworkflows_streamedid', 'content_type_id') - - # Deleting field 'StreamedID.content_id' - db.delete_column('ietfworkflows_streamedid', 'content_id') - - - - def backwards(self, orm): - - # Adding field 'StreamedID.content_type' - db.add_column('ietfworkflows_streamedid', 'content_type', orm['ietfworkflows.streamedid:content_type']) - - # Adding field 'StreamedID.content_id' - db.add_column('ietfworkflows_streamedid', 'content_id', orm['ietfworkflows.streamedid:content_id']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0012_refactor_stream_group_descrition.py b/ietf/ietfworkflows/migrations/0012_refactor_stream_group_descrition.py deleted file mode 100644 index e5a61fcbd..000000000 --- a/ietf/ietfworkflows/migrations/0012_refactor_stream_group_descrition.py +++ /dev/null @@ -1,222 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding field 'Stream.group_chair_attribute' - db.add_column('ietfworkflows_stream', 'group_chair_attribute', orm['ietfworkflows.stream:group_chair_attribute']) - - # Adding field 'Stream.document_group_attribute' - db.add_column('ietfworkflows_stream', 'document_group_attribute', orm['ietfworkflows.stream:document_group_attribute']) - - # Deleting field 'Stream.group_chair_model' - db.delete_column('ietfworkflows_stream', 'group_chair_model') - - # Deleting field 'Stream.with_groups' - db.delete_column('ietfworkflows_stream', 'with_groups') - - # Deleting field 'Stream.group_model' - db.delete_column('ietfworkflows_stream', 'group_model') - - - - def backwards(self, orm): - - # Deleting field 'Stream.group_chair_attribute' - db.delete_column('ietfworkflows_stream', 'group_chair_attribute') - - # Deleting field 'Stream.document_group_attribute' - db.delete_column('ietfworkflows_stream', 'document_group_attribute') - - # Adding field 'Stream.group_chair_model' - db.add_column('ietfworkflows_stream', 'group_chair_model', orm['ietfworkflows.stream:group_chair_model']) - - # Adding field 'Stream.with_groups' - db.add_column('ietfworkflows_stream', 'with_groups', orm['ietfworkflows.stream:with_groups']) - - # Adding field 'Stream.group_model' - db.add_column('ietfworkflows_stream', 'group_model', orm['ietfworkflows.stream:group_model']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'document_group_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'group_chair_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0013_add_stream_delegates.py b/ietf/ietfworkflows/migrations/0013_add_stream_delegates.py deleted file mode 100644 index 2647539a7..000000000 --- a/ietf/ietfworkflows/migrations/0013_add_stream_delegates.py +++ /dev/null @@ -1,208 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StreamDelegate' - db.create_table('ietfworkflows_streamdelegate', ( - ('id', orm['ietfworkflows.streamdelegate:id']), - ('stream', orm['ietfworkflows.streamdelegate:stream']), - ('person', orm['ietfworkflows.streamdelegate:person']), - )) - db.send_create_signal('ietfworkflows', ['StreamDelegate']) - - - - def backwards(self, orm): - - # Deleting model 'StreamDelegate' - db.delete_table('ietfworkflows_streamdelegate') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'document_group_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'group_chair_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0014_change_alt_streams_states.py b/ietf/ietfworkflows/migrations/0014_change_alt_streams_states.py deleted file mode 100644 index a6ef0232e..000000000 --- a/ietf/ietfworkflows/migrations/0014_change_alt_streams_states.py +++ /dev/null @@ -1,234 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - no_dry_run = True - - def forwards(self, orm): - # Remove all 'Published RFC' status - for state in orm['workflows.state'].objects.filter(name='Published RFC'): - state.delete() - - - def backwards(self, orm): - "Write your backwards migration here" - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'document_group_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'group_chair_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'permissions.role': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateinheritanceblock': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.statepermissionrelation': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - }, - 'workflows.workflowmodelrelation': { - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wmrs'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wors'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowpermissionrelation': { - 'Meta': {'unique_together': "(('workflow', 'permission'),)"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': "orm['permissions.Permission']"}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"}) - } - } - - complete_apps = ['ietfworkflows', 'workflows'] diff --git a/ietf/ietfworkflows/migrations/__init__.py b/ietf/ietfworkflows/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/ietfworkflows/models.py b/ietf/ietfworkflows/models.py deleted file mode 100644 index 5a455a6b3..000000000 --- a/ietf/ietfworkflows/models.py +++ /dev/null @@ -1,268 +0,0 @@ -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -from django.db import models -from django.utils.translation import ugettext_lazy as _ -from django.conf import settings - -from ietf.idtracker.models import PersonOrOrgInfo, InternetDraft, Role, IRTF -from ietf.utils.admin import admin_link -from workflows.models import Workflow, State, StateObjectRelation -from permissions.models import Permission - - -class ObjectHistoryEntry(models.Model): - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="workflow_history", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - date = models.DateTimeField(_('Date'), auto_now_add=True) - comment = models.TextField(_('Comment')) - person = models.ForeignKey(PersonOrOrgInfo) - - class Meta: - ordering = ('-date', ) - - def get_real_instance(self): - if hasattr(self, '_real_instance'): - return self._real_instance - for i in ('objectworkflowhistoryentry', 'objectannotationtaghistoryentry', 'objectstreamhistoryentry'): - try: - real_instance = getattr(self, i, None) - if real_instance: - self._real_instance = real_instance - return real_instance - except models.ObjectDoesNotExist: - continue - self._real_instance = self - return self - - -class ObjectWorkflowHistoryEntry(ObjectHistoryEntry): - from_state = models.CharField(_('From state'), max_length=100) - to_state = models.CharField(_('To state'), max_length=100) - - def describe_change(self): - html = '

      ' - html += 'Changed state %s to %s' % (self.from_state, self.to_state) - html += '

      ' - return html - - -class ObjectAnnotationTagHistoryEntry(ObjectHistoryEntry): - setted = models.TextField(_('Setted tags'), blank=True, null=True) - unsetted = models.TextField(_('Unsetted tags'), blank=True, null=True) - - def describe_change(self): - html = '' - if self.setted: - html += '

      ' - html += 'Annotation tags set: ' - html += self.setted - html += '

      ' - if self.unsetted: - html += '

      ' - html += 'Annotation tags reset: ' - html += self.unsetted - html += '

      ' - return html - - -class ObjectStreamHistoryEntry(ObjectHistoryEntry): - from_stream = models.TextField(_('From stream'), blank=True, null=True) - to_stream = models.TextField(_('To stream'), blank=True, null=True) - - def describe_change(self): - html = '

      ' - html += 'Changed doc from stream %s to %s' % (self.from_stream, self.to_stream) - html += '

      ' - return html - - -class StateDescription(models.Model): - state = models.ForeignKey(State) - definition = models.TextField() - order = models.PositiveIntegerField() - - class Meta: - ordering = ('order', ) - - def __unicode__(self): - return unicode(self.state) - - -class AnnotationTag(models.Model): - name = models.CharField(_(u"Name"), max_length=100) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="annotation_tags") - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"), blank=True, null=True) - - class Meta: - ordering = ('name', ) - - def __unicode__(self): - return self.name - - -class AnnotationTagObjectRelation(models.Model): - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="annotation_tags", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - annotation_tag = models.ForeignKey(AnnotationTag, verbose_name=_(u"Annotation tag")) - - -class StateObjectRelationMetadata(models.Model): - relation = models.ForeignKey(StateObjectRelation) - from_date = models.DateTimeField(_('Initial date'), blank=True, null=True) - estimated_date = models.DateTimeField(_('Estimated date'), blank=True, null=True) - - -class WGWorkflow(Workflow): - selected_states = models.ManyToManyField(State, blank=True, null=True) - selected_tags = models.ManyToManyField(AnnotationTag, blank=True, null=True) - - class Meta: - verbose_name = 'IETF Workflow' - verbose_name_plural = 'IETF Workflows' - - def get_tags(self): - tags = self.annotation_tags.all() - if tags.count(): - return tags - else: - return self.selected_tags.all() - - def get_states(self): - states = self.states.all() - if states.count(): - return states - else: - return self.selected_states.all() - - -class Stream(models.Model): - name = models.CharField(_(u"Name"), max_length=100) - document_group_attribute = models.CharField(_(u'Document group attribute'), max_length=255, blank=True, null=True) - group_chair_attribute = models.CharField(_(u'Group chair attribute'), max_length=255, blank=True, null=True) - workflow = models.ForeignKey(WGWorkflow) - - def __unicode__(self): - return u'%s stream' % self.name - workflow_link = admin_link('workflow') - - def _irtf_group(self, document): - filename = document.filename.split('-') - if len(filename) > 2 and filename[0] == 'draft' and filename[1] == 'irtf': - try: - return IRTF.objects.get(acronym=filename[2]) - except IRTF.DoesNotExist: - return None - return None - - def _irtf_chairs_for_document(self, document): - group = self._irtf_group(document) - if not group: - return [] - chairs = [i.person for i in group.chairs()] - chairs.append(Role.objects.get(pk=Role.IRTF_CHAIR).person) - return chairs - - def _ietf_delegates_for_document(self, document): - group = self.get_group_for_document(document) - if not group: - return False - return [i.person for i in group.wgdelegate_set.all()] - - def get_group_for_document(self, document): - if hasattr(self, '_%s_group' % self.name.lower()): - return getattr(self, '_%s_group' % self.name.lower())(document) - - if not self.document_group_attribute: - return None - attr = None - obj = document - for attr_name in self.document_group_attribute.split('.'): - attr = getattr(obj, attr_name, None) - if not attr: - return None - if callable(attr): - attr = attr() - obj = attr - return attr - - def get_chairs_for_document(self, document): - if hasattr(self, '_%s_chairs_for_document' % self.name.lower()): - return getattr(self, '_%s_chairs_for_document' % self.name.lower())(document) - - group = self.get_group_for_document(document) - if not group or not self.group_chair_attribute: - return [] - attr = None - obj = group - for attr_name in self.group_chair_attribute.split('.'): - attr = getattr(obj, attr_name, None) - if not attr: - return None - if callable(attr): - attr = attr() - obj = attr - return attr - - def get_delegates_for_document(self, document): - delegates = [] - if hasattr(self, '_%s_delegates_for_document' % self.name.lower()): - delegates = getattr(self, '_%s_delegates_for_document' % self.name.lower())(document) - delegates += [i.person for i in self.streamdelegate_set.all()] - return delegates - - def _ise_chairs_for_document(self, document): - return self._ise_stream_chairs() - - def _ise_stream_chairs(self): - chairs = [] - try: - chairs.append(Role.objects.get(role_name='ISE').person) - except Role.DoesNotExist: - pass - return chairs - - def get_chairs(self): - chairs = [] - if hasattr(self, '_%s_stream_chairs' % self.name.lower()): - chairs += list(getattr(self, '_%s_stream_chairs' % self.name.lower())()) - - role_key = getattr(Role, '%s_CHAIR' % self.name.upper(), None) - if role_key: - try: - chairs.append(Role.objects.get(pk=role_key).person) - except Role.DoesNotExist: - pass - return list(set(chairs)) - - def get_delegates(self): - delegates = [] - if hasattr(self, '_%s_stream_delegates' % self.name.lower()): - delegates += list(getattr(self, '_%s_stream_delegates' % self.name.lower())()) - delegates += [i.person for i in StreamDelegate.objects.filter(stream=self)] - return list(set(delegates)) - - def check_chair(self, person): - return person in self.get_chairs() - - def check_delegate(self, person): - return person in self.get_delegates() - - -class StreamedID(models.Model): - draft = models.OneToOneField(InternetDraft) - stream = models.ForeignKey(Stream, blank=True, null=True) - - def get_group(self): - return self.stream.get_group_for_document(self.draft) - - -class StreamDelegate(models.Model): - stream = models.ForeignKey(Stream) - person = models.ForeignKey(PersonOrOrgInfo) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.name.proxy import StreamProxy as Stream diff --git a/ietf/ietfworkflows/streams.py b/ietf/ietfworkflows/streams.py deleted file mode 100644 index 9540a2825..000000000 --- a/ietf/ietfworkflows/streams.py +++ /dev/null @@ -1,102 +0,0 @@ -from django.db import models -from django.conf import settings -from ietf.idtracker.models import InternetDraft - -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper -from ietf.ietfworkflows.models import StreamedID, Stream - - -def get_streamed_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - class Dummy: pass - o = Dummy() - o.draft = draft - o.stream = super(InternetDraft, draft).stream - o.group = draft.group - o.get_group = lambda: draft.group - return o - - if not draft: - return None - try: - return draft.streamedid - except StreamedID.DoesNotExist: - return None - - -def get_stream_from_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - s = super(InternetDraft, draft).stream - if s: - s.with_groups = s.slug in ["ietf", "irtf"] - return s - - streamedid = get_streamed_draft(draft) - if streamedid: - return streamedid.stream - return False - - -def get_stream_by_name(stream_name): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - - try: - return Stream.objects.get(name=stream_name) - except Stream.DoesNotExist: - return None - - -def get_stream_from_id(stream_id): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - - try: - return Stream.objects.get(id=stream_id) - except Stream.DoesNotExist: - return None - - -def _set_stream_automatically(draft, stream): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - (streamed, created) = StreamedID.objects.get_or_create(draft=draft) - if created: - streamed.stream = stream - streamed.save() - return - - -def get_stream_from_wrapper(idrfc_wrapper): - idwrapper = None - if isinstance(idrfc_wrapper, IdRfcWrapper): - idwrapper = idrfc_wrapper.id - elif isinstance(idrfc_wrapper, IdWrapper): - idwrapper = idrfc_wrapper - if not idwrapper: - return None - draft = idwrapper._draft - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return super(InternetDraft, draft).stream - - stream = get_stream_from_draft(draft) - if stream == False: - stream_id = idwrapper.stream_id() - stream = get_stream_from_id(stream_id) - _set_stream_automatically(draft, stream) - return stream - else: - return stream - return None - - -def set_stream_for_draft(draft, stream): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - - (streamed, created) = StreamedID.objects.get_or_create(draft=draft) - if streamed.stream != stream: - streamed.stream = stream - streamed.group = None - streamed.save() - return streamed.stream diff --git a/ietf/ietfworkflows/templatetags/.gitignore b/ietf/ietfworkflows/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/templatetags/__init__.py b/ietf/ietfworkflows/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/ietfworkflows/templatetags/ietf_streams.py b/ietf/ietfworkflows/templatetags/ietf_streams.py deleted file mode 100644 index 5d8b5ca51..000000000 --- a/ietf/ietfworkflows/templatetags/ietf_streams.py +++ /dev/null @@ -1,112 +0,0 @@ -from django import template -from django.conf import settings -from django.core.urlresolvers import reverse as urlreverse - -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper -from ietf.ietfworkflows.utils import (get_workflow_for_draft, - get_state_for_draft) -from ietf.ietfworkflows.streams import get_stream_from_wrapper -from ietf.ietfworkflows.models import Stream -from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, - is_chair_of_stream, can_adopt) - -register = template.Library() - - -@register.inclusion_tag('ietfworkflows/stream_state.html', takes_context=True) -def stream_state(context, doc): - data = {} - stream = get_stream_from_wrapper(doc) - data.update({'stream': stream}) - if not stream: - return data - - idwrapper = None - if isinstance(doc, IdRfcWrapper): - idwrapper = doc.id - elif isinstance(doc, IdWrapper): - idwrapper = doc - if not idwrapper: - return data - - draft = getattr(idwrapper, '_draft', None) - if not draft: - return data - - workflow = get_workflow_for_draft(draft) - state = get_state_for_draft(draft) - - data.update({'workflow': workflow, - 'draft': draft, - 'state': state, - 'milestones': draft.groupmilestone_set.filter(state="active") - }) - - return data - - -@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True) -def workflow_history_entry(context, entry): - real_entry = entry.get_real_instance() - return {'entry': real_entry, - 'entry_class': real_entry.__class__.__name__.lower()} - - -@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True) -def edit_actions(context, wrapper): - request = context.get('request', None) - user = request and request.user - if not user: - return {} - idwrapper = None - if isinstance(wrapper, IdRfcWrapper): - idwrapper = wrapper.id - elif isinstance(wrapper, IdWrapper): - idwrapper = wrapper - if not idwrapper: - return None - doc = wrapper - draft = wrapper._draft - - actions = [] - if can_adopt(user, draft): - actions.append(("Adopt in WG", urlreverse('edit_adopt', kwargs=dict(name=doc.draft_name)))) - - if can_edit_state(user, draft): - actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name)))) - if draft.stream_id in ("iab", "ise", "irtf"): - actions.append(("Request publication", urlreverse('doc_request_publication', kwargs=dict(name=doc.draft_name)))) - - return dict(actions=actions) - - -class StreamListNode(template.Node): - - def __init__(self, user, var_name): - self.user = user - self.var_name = var_name - - def render(self, context): - user = self.user.resolve(context) - streams = [] - for i in Stream.objects.all(): - if "Legacy" in i.name: - continue - if is_chair_of_stream(user, i): - streams.append(i) - context.update({self.var_name: streams}) - return '' - - -@register.tag -def get_user_managed_streams(parser, token): - firstbits = token.contents.split(None, 2) - if len(firstbits) != 3: - raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments") - user = parser.compile_filter(firstbits[1]) - lastbits_reversed = firstbits[2][::-1].split(None, 2) - if lastbits_reversed[1][::-1] != 'as': - raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must" - " be 'as'") - var_name = lastbits_reversed[0][::-1] - return StreamListNode(user, var_name) diff --git a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py b/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py deleted file mode 100644 index 852911126..000000000 --- a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py +++ /dev/null @@ -1,102 +0,0 @@ -from django import template -from django.conf import settings -from django.core.urlresolvers import reverse as urlreverse - -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper -from ietf.ietfworkflows.utils import (get_workflow_for_draft, - get_state_for_draft) -from ietf.ietfworkflows.streams import get_stream_from_wrapper -from ietf.ietfworkflows.models import Stream -from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, - is_chair_of_stream, can_adopt) - -register = template.Library() - - -@register.inclusion_tag('ietfworkflows/stream_state_redesign.html', takes_context=True) -def stream_state(context, doc): - data = {} - stream = doc.stream - data.update({'stream': stream}) - if not stream: - return data - - if doc.type.slug != 'draft': - return data - - state = get_state_for_draft(doc) - - data.update({ - 'state': state, - 'doc': doc, - }) - - return data - - -@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True) -def workflow_history_entry(context, entry): - real_entry = entry.get_real_instance() - return {'entry': real_entry, - 'entry_class': real_entry.__class__.__name__.lower()} - - -@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True) -def edit_actions(context, wrapper): - request = context.get('request', None) - user = request and request.user - if not user: - return {} - idwrapper = None - if isinstance(wrapper, IdRfcWrapper): - idwrapper = wrapper.id - elif isinstance(wrapper, IdWrapper): - idwrapper = wrapper - if not idwrapper: - return None - doc = wrapper - draft = wrapper._draft - - actions = [] - if can_adopt(user, draft): - actions.append(("Adopt in WG", urlreverse('edit_adopt', kwargs=dict(name=doc.draft_name)))) - - if can_edit_state(user, draft): - actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name)))) - - if can_edit_stream(user, draft): - actions.append(("Change stream", urlreverse('edit_stream', kwargs=dict(name=doc.draft_name)))) - - return dict(actions=actions) - - -class StreamListNode(template.Node): - - def __init__(self, user, var_name): - self.user = user - self.var_name = var_name - - def render(self, context): - user = self.user.resolve(context) - streams = [] - for i in Stream.objects.all(): - if "Legacy" in i.name: - continue - if is_chair_of_stream(user, i): - streams.append(i) - context.update({self.var_name: streams}) - return '' - - -@register.tag -def get_user_managed_streams(parser, token): - firstbits = token.contents.split(None, 2) - if len(firstbits) != 3: - raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments") - user = parser.compile_filter(firstbits[1]) - lastbits_reversed = firstbits[2][::-1].split(None, 2) - if lastbits_reversed[1][::-1] != 'as': - raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must" - " be 'as'") - var_name = lastbits_reversed[0][::-1] - return StreamListNode(user, var_name) diff --git a/ietf/ietfworkflows/tests.py b/ietf/ietfworkflows/tests.py deleted file mode 100644 index ad467a9e7..000000000 --- a/ietf/ietfworkflows/tests.py +++ /dev/null @@ -1,188 +0,0 @@ -import datetime, os, shutil - -from django.conf import settings -from django.contrib.auth.models import User -from django.core.urlresolvers import reverse as urlreverse -import django.test -from StringIO import StringIO -from pyquery import PyQuery - -from ietf.utils.test_utils import login_testing_unauthorized -from ietf.utils.test_data import make_test_data -from ietf.utils.mail import outbox - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.person.models import Person, Email - from ietf.group.models import Group, Role - from ietf.doc.models import Document, State - from ietf.doc.utils import * - from ietf.name.models import DocTagName - -class EditStreamInfoTestCase(django.test.TestCase): - fixtures = ['names'] - - def test_adopt_document(self): - draft = make_test_data() - draft.stream = None - draft.group = Group.objects.get(type="individ") - draft.save() - draft.unset_state("draft-stream-ietf") - - url = urlreverse('edit_adopt', kwargs=dict(name=draft.name)) - login_testing_unauthorized(self, "marschairman", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[type=submit][value*=adopt]')), 1) - self.assertEquals(len(q('form select[name="group"] option')), 1) # we can only select "mars" - - # adopt in mars WG - mailbox_before = len(outbox) - events_before = draft.docevent_set.count() - r = self.client.post(url, - dict(comment="some comment", - group=Group.objects.get(acronym="mars").pk, - weeks="10")) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.group.acronym, "mars") - self.assertEquals(draft.stream_id, "ietf") - self.assertEquals(draft.docevent_set.count() - events_before, 4) - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) - self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) - self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) - - def test_set_tags(self): - draft = make_test_data() - draft.tags = DocTagName.objects.filter(slug="w-expert") - draft.group.unused_tags.add("w-refdoc") - - url = urlreverse('edit_state', kwargs=dict(name=draft.name)) - login_testing_unauthorized(self, "marschairman", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - # make sure the unused tags are hidden - unused = draft.group.unused_tags.values_list("slug", flat=True) - for t in q("input[name=tags]"): - self.assertTrue(t.attrib["value"] not in unused) - - # set tags - mailbox_before = len(outbox) - events_before = draft.docevent_set.count() - r = self.client.post(url, - dict(comment="some comment", - weeks="10", - tags=["need-aut", "sheph-u"], - only_tags="1", - # unused but needed for validation - new_state=draft.get_state("draft-stream-%s" % draft.stream_id).pk, - )) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.tags.count(), 2) - self.assertEquals(draft.tags.filter(slug="w-expert").count(), 0) - self.assertEquals(draft.tags.filter(slug="need-aut").count(), 1) - self.assertEquals(draft.tags.filter(slug="sheph-u").count(), 1) - self.assertEquals(draft.docevent_set.count() - events_before, 2) - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("tags changed" in outbox[-1]["Subject"].lower()) - self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) - self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) - self.assertTrue("plain@example.com" in unicode(outbox[-1])) - - def test_set_state(self): - draft = make_test_data() - - url = urlreverse('edit_state', kwargs=dict(name=draft.name)) - login_testing_unauthorized(self, "marschairman", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - # make sure the unused states are hidden - unused = draft.group.unused_states.values_list("pk", flat=True) - for t in q("select[name=new_state]").find("option[name=tags]"): - self.assertTrue(t.attrib["value"] not in unused) - self.assertEquals(len(q('select[name=new_state]')), 1) - - old_state = draft.get_state("draft-stream-%s" % draft.stream_id ) - new_state = State.objects.get(used=True, type="draft-stream-%s" % draft.stream_id, slug="parked") - self.assertTrue(old_state!=new_state) - mailbox_before = len(outbox) - events_before = draft.docevent_set.count() - - # First make sure cancel doesn't change anything - r = self.client.post(url, - dict(comment="some comment", - weeks="10", - tags=[x.pk for x in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))], - new_state=new_state.pk, - cancel="1", - )) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id), old_state) - - # Set new state - r = self.client.post(url, - dict(comment="some comment", - weeks="10", - tags=[x.pk for x in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))], - new_state=new_state.pk, - )) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id), new_state) - self.assertEquals(draft.docevent_set.count() - events_before, 2) - reminder = DocReminder.objects.filter(event__doc=draft, type="stream-s") - self.assertEquals(len(reminder), 1) - due = datetime.datetime.now() + datetime.timedelta(weeks=10) - self.assertTrue(due - datetime.timedelta(days=1) <= reminder[0].due <= due + datetime.timedelta(days=1)) - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) - self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) - self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) - - def test_manage_stream_delegates(self): - make_test_data() - - url = urlreverse('stream_delegates', kwargs=dict(stream_name="IETF")) - login_testing_unauthorized(self, "secretary", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('input[type=submit][value*=Add]')), 1) - - delegate = Email.objects.get(address="plain@example.com") - - # add delegate - r = self.client.post(url, - dict(email=delegate.address)) - self.assertEquals(r.status_code, 200) - - self.assertEquals(Role.objects.filter(group__acronym="ietf", name="delegate", person__email__address=delegate.address).count(), 1) - - # remove delegate again - r = self.client.post(url, - dict(remove_delegate=[delegate.person.pk], - delete="1")) - self.assertEquals(r.status_code, 200) - - self.assertEquals(Role.objects.filter(group__acronym="ietf", name="delegate", person__email__address=delegate.address).count(), 0) - -if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - # the above tests only work with the new schema - del EditStreamInfoTestCase diff --git a/ietf/ietfworkflows/urls.py b/ietf/ietfworkflows/urls.py deleted file mode 100644 index af7a885d4..000000000 --- a/ietf/ietfworkflows/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright The IETF Trust 2008, All Rights Reserved - -from django.conf.urls.defaults import patterns, url -from django.views.generic.simple import redirect_to - -urlpatterns = patterns('ietf.ietfworkflows.views', - url(r'^(?P[^/]+)/history/$', 'stream_history', name='stream_history'), - url(r'^(?P[^/]+)/edit/adopt/$', 'edit_adopt', name='edit_adopt'), - # FIXME: the name edit_state is far too generic - url(r'^(?P[^/]+)/edit/state/$', 'edit_state', name='edit_state'), - url(r'^(?P[^/]+)/edit/stream/$', redirect_to, { 'url': '/doc/%(name)s/edit/info/'}) , - url(r'^delegates/(?P[^/]+)/$', 'stream_delegates', name='stream_delegates'), -) diff --git a/ietf/ietfworkflows/utils.py b/ietf/ietfworkflows/utils.py deleted file mode 100644 index 5d724be4d..000000000 --- a/ietf/ietfworkflows/utils.py +++ /dev/null @@ -1,466 +0,0 @@ -import copy -import datetime - -from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.core.mail import EmailMessage -from django.template.loader import render_to_string -from django.template.defaultfilters import pluralize - -from workflows.models import State, StateObjectRelation -from workflows.utils import (get_workflow_for_object, set_workflow_for_object, - get_state, set_state) - -from ietf.ietfworkflows.streams import (get_streamed_draft, get_stream_from_draft, - set_stream_for_draft) -from ietf.ietfworkflows.models import (WGWorkflow, AnnotationTagObjectRelation, - AnnotationTag, ObjectAnnotationTagHistoryEntry, - ObjectHistoryEntry, StateObjectRelationMetadata, - ObjectWorkflowHistoryEntry, ObjectStreamHistoryEntry) -from ietf.idtracker.models import InternetDraft -from ietf.utils.mail import send_mail -from ietf.doc.models import Document, DocEvent, save_document_in_history, DocReminder, DocReminderTypeName -from ietf.group.models import Role - -WAITING_WRITEUP = 'WG Consensus: Waiting for Write-Up' -FOLLOWUP_TAG = 'Doc Shepherd Follow-up Underway' - - -def get_default_workflow_for_wg(): - try: - workflow = WGWorkflow.objects.get(name='Default WG Workflow') - return workflow - except WGWorkflow.DoesNotExist: - return None - - -def clone_transition(transition): - new = copy.copy(transition) - new.pk = None - new.save() - - # Reference original initial states - for state in transition.states.all(): - new.states.add(state) - return new - - -def clone_workflow(workflow, name): - new = WGWorkflow.objects.create(name=name, initial_state=workflow.initial_state) - - # Reference default states - for state in workflow.states.all(): - new.selected_states.add(state) - - # Reference default annotation tags - for tag in workflow.annotation_tags.all(): - new.selected_tags.add(tag) - - # Reference cloned transitions - for transition in workflow.transitions.all(): - new.transitions.add(clone_transition(transition)) - return new - - -def get_workflow_for_wg(wg, default=None): - workflow = get_workflow_for_object(wg) - try: - workflow = workflow and workflow.wgworkflow - except WGWorkflow.DoesNotExist: - workflow = None - if not workflow: - if default: - workflow = default - else: - workflow = get_default_workflow_for_wg() - if not workflow: - return None - workflow = clone_workflow(workflow, name='%s workflow' % wg) - set_workflow_for_object(wg, workflow) - return workflow - - -def get_workflow_for_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return True if get_streamed_draft(draft) else None - - workflow = get_workflow_for_object(draft) - try: - workflow = workflow and workflow.wgworkflow - except WGWorkflow.DoesNotExist: - workflow = None - if not workflow: - streamed_draft = get_streamed_draft(draft) - if not streamed_draft or not streamed_draft.stream: - return None - stream = streamed_draft.stream - if stream.document_group_attribute: - group = streamed_draft.get_group() - if not group: - return None - else: - workflow = get_workflow_for_wg(group, streamed_draft.stream.workflow) - else: - workflow = stream.workflow - set_workflow_for_object(draft, workflow) - return workflow - - -def get_workflow_history_for_draft(draft, entry_type=None): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.proxy import ObjectHistoryEntryProxy - return ObjectHistoryEntryProxy.objects.filter(doc=draft).order_by('-time', '-id').select_related('by') - - ctype = ContentType.objects.get_for_model(draft) - filter_param = {'content_type': ctype, - 'content_id': draft.pk} - if entry_type: - filter_param.update({'%s__isnull' % entry_type: False}) - history = ObjectHistoryEntry.objects.filter(**filter_param).\ - select_related('objectworkflowhistoryentry', 'objectannotationtaghistoryentry', - 'objectstreamhistoryentry') - return history - - -def get_annotation_tags_for_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.name.proxy import AnnotationTagObjectRelationProxy - from ietf.doc.utils import get_tags_for_stream_id - return AnnotationTagObjectRelationProxy.objects.filter(document=draft.pk, slug__in=get_tags_for_stream_id(draft.stream_id)) - - ctype = ContentType.objects.get_for_model(draft) - tags = AnnotationTagObjectRelation.objects.filter(content_type=ctype, content_id=draft.pk) - return tags - - -def get_state_for_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return draft.get_state("draft-stream-%s" % draft.stream_id) - return get_state(draft) - - -def get_state_by_name(state_name): - try: - return State.objects.get(used=True, name=state_name) - except State.DoesNotExist: - return None - - -def get_annotation_tag_by_name(tag_name): - try: - return AnnotationTag.objects.get(name=tag_name) - except AnnotationTag.DoesNotExist: - return None - - -def set_tag(obj, tag): - ctype = ContentType.objects.get_for_model(obj) - (relation, created) = AnnotationTagObjectRelation.objects.get_or_create( - content_type=ctype, - content_id=obj.pk, - annotation_tag=tag) - return relation - - -def set_tag_by_name(obj, tag_name): - try: - tag = AnnotationTag.objects.get(name=tag_name) - return set_tag(obj, tag) - except AnnotationTag.DoesNotExist: - return None - - -def reset_tag(obj, tag): - ctype = ContentType.objects.get_for_model(obj) - try: - tag_relation = AnnotationTagObjectRelation.objects.get( - content_type=ctype, - content_id=obj.pk, - annotation_tag=tag) - tag_relation.delete() - return True - except AnnotationTagObjectRelation.DoesNotExist: - return False - - -def reset_tag_by_name(obj, tag_name): - try: - tag = AnnotationTag.objects.get(name=tag_name) - return reset_tag(obj, tag) - except AnnotationTag.DoesNotExist: - return False - - -def set_state_for_draft(draft, state, estimated_date=None): - workflow = get_workflow_for_draft(draft) - if state in workflow.get_states(): - set_state(draft, state) - relation = StateObjectRelation.objects.get( - content_type=ContentType.objects.get_for_model(draft), - content_id=draft.pk) - metadata = StateObjectRelationMetadata.objects.get_or_create(relation=relation)[0] - metadata.from_date = datetime.date.today() - metadata.to_date = estimated_date - metadata.save() - return state - return False - - -def notify_entry(entry, template, extra_notify=[]): - doc = entry.content - wg = doc.group.ietfwg - mail_list = set(['%s <%s>' % i.person.email() for i in wg.wgchair_set.all() if i.person.email()]) - mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in wg.wgdelegate_set.all() if i.person.email()]) - mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in doc.authors.all() if i.person.email()]) - mail_list = mail_list.union(extra_notify) - mail_list = list(mail_list) - - subject = 'Annotation tags have changed for draft %s' % doc - body = render_to_string(template, {'doc': doc, - 'entry': entry, - }) - mail = EmailMessage(subject=subject, - body=body, - to=mail_list, - from_email=settings.DEFAULT_FROM_EMAIL) - # Only send emails if we are not debug mode - if not settings.DEBUG: - mail.send() - return mail - - -def notify_tag_entry(entry, extra_notify=[]): - return notify_entry(entry, 'ietfworkflows/annotation_tags_updated_mail.txt', extra_notify) - - -def notify_state_entry(entry, extra_notify=[]): - return notify_entry(entry, 'ietfworkflows/state_updated_mail.txt', extra_notify) - - -def notify_stream_entry(entry, extra_notify=[]): - return notify_entry(entry, 'ietfworkflows/stream_updated_mail.txt', extra_notify) - -def get_notification_receivers(doc, extra_notify): - persons = set() - res = [] - for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")): - res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address)) - persons.add(r.person) - - for email in doc.authors.all(): - if email.person not in persons: - res.append(email.formatted_email()) - persons.add(email.person) - - for x in extra_notify: - if not x in res: - res.append(x) - - return res - -def get_pubreq_receivers(doc, extra_notify): - res = [u'"IESG Secretary" ', ] - - for r in Role.objects.filter(person=doc.group.ad,name__slug='ad'): - res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address)) - - for x in extra_notify: - if not x in res: - res.append(x) - - return res - -def get_pubreq_cc_receivers(doc): - res = [] - - for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")): - res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address)) - - return res - -def update_tags(request, obj, comment, person, set_tags=[], reset_tags=[], extra_notify=[]): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - doc = Document.objects.get(pk=obj.pk) - save_document_in_history(doc) - - obj.tags.remove(*reset_tags) - obj.tags.add(*set_tags) - - doc.time = datetime.datetime.now() - - e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc) - l = [] - if set_tags: - l.append(u"Annotation tag%s %s set." % (pluralize(set_tags), ", ".join(x.name for x in set_tags))) - if reset_tags: - l.append(u"Annotation tag%s %s cleared." % (pluralize(reset_tags), ", ".join(x.name for x in reset_tags))) - e.desc = " ".join(l) - e.save() - - receivers = get_notification_receivers(doc, extra_notify) - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"Annotations tags changed for draft %s" % doc.name, - 'ietfworkflows/annotation_tags_updated_mail.txt', - dict(doc=doc, - entry=dict(setted=", ".join(x.name for x in set_tags), - unsetted=", ".join(x.name for x in reset_tags), - change_date=doc.time, - person=person, - comment=comment))) - return - - ctype = ContentType.objects.get_for_model(obj) - setted = [] - resetted = [] - for tag in set_tags: - if isinstance(tag, basestring): - if set_tag_by_name(obj, tag): - setted.append(tag) - else: - if set_tag(obj, tag): - setted.append(tag.name) - for tag in reset_tags: - if isinstance(tag, basestring): - if reset_tag_by_name(obj, tag): - resetted.append(tag) - else: - if reset_tag(obj, tag): - resetted.append(tag.name) - entry = ObjectAnnotationTagHistoryEntry.objects.create( - content_type=ctype, - content_id=obj.pk, - setted=','.join(setted), - unsetted=','.join(resetted), - date=datetime.datetime.now(), - comment=comment, - person=person) - notify_tag_entry(entry, extra_notify) - - -def update_state(request, doc, comment, person, to_state, estimated_date=None, extra_notify=[]): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - doc = Document.objects.get(pk=doc.pk) - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - from_state = doc.get_state("draft-stream-%s" % doc.stream_id) - doc.set_state(to_state) - - e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc) - e.desc = u"%s changed to %s from %s" % (to_state.type.label, to_state, from_state) - e.save() - - # reminder - reminder_type = DocReminderTypeName.objects.get(slug="stream-s") - try: - reminder = DocReminder.objects.get(event__doc=doc, type=reminder_type, - active=True) - except DocReminder.DoesNotExist: - reminder = None - - if estimated_date: - if not reminder: - reminder = DocReminder(type=reminder_type) - - reminder.event = e - reminder.due = estimated_date - reminder.active = True - reminder.save() - elif reminder: - reminder.active = False - reminder.save() - - receivers = get_notification_receivers(doc, extra_notify) - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"State changed for draft %s" % doc.name, - 'ietfworkflows/state_updated_mail.txt', - dict(doc=doc, - entry=dict(from_state=from_state, - to_state=to_state, - transition_date=doc.time, - person=person, - comment=comment))) - - if (to_state.slug=='sub-pub'): - receivers = get_pubreq_receivers(doc, extra_notify) - cc_receivers = get_pubreq_cc_receivers(doc) - - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"Publication has been requested for draft %s" % doc.name, - 'ietfworkflows/state_updated_mail.txt', - dict(doc=doc, - entry=dict(from_state=from_state, - to_state=to_state, - transition_date=doc.time, - person=person, - comment=comment)), cc=cc_receivers) - - return - - ctype = ContentType.objects.get_for_model(doc) - from_state = get_state_for_draft(doc) - to_state = set_state_for_draft(doc, to_state, estimated_date) - if not to_state: - return False - entry = ObjectWorkflowHistoryEntry.objects.create( - content_type=ctype, - content_id=doc.pk, - from_state=from_state and from_state.name or '', - to_state=to_state and to_state.name or '', - date=datetime.datetime.now(), - comment=comment, - person=person) - notify_state_entry(entry, extra_notify) - - -def update_stream(request, doc, comment, person, to_stream, extra_notify=[]): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - doc = Document.objects.get(pk=doc.pk) - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - from_stream = doc.stream - doc.stream = to_stream - doc.save() - - e = DocEvent(type="changed_stream", time=doc.time, by=person, doc=doc) - e.desc = u"Stream changed to %s" % to_stream.name - if from_stream: - e.desc += u"from %s" % from_stream.name - e.save() - - receivers = get_notification_receivers(doc, extra_notify) - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"Stream changed for draft %s" % doc.name, - 'ietfworkflows/stream_updated_mail.txt', - dict(doc=doc, - entry=dict(from_stream=from_stream, - to_stream=to_stream, - transition_date=doc.time, - person=person, - comment=comment))) - return - - ctype = ContentType.objects.get_for_model(doc) - from_stream = get_stream_from_draft(doc) - to_stream = set_stream_for_draft(doc, to_stream) - entry = ObjectStreamHistoryEntry.objects.create( - content_type=ctype, - content_id=doc.pk, - from_stream=from_stream and from_stream.name or '', - to_stream=to_stream and to_stream.name or '', - date=datetime.datetime.now(), - comment=comment, - person=person) - notify_stream_entry(entry, extra_notify) - - -def get_full_info_for_draft(draft): - return dict( - streamed=get_streamed_draft(draft), - stream=get_stream_from_draft(draft), - workflow=get_workflow_for_draft(draft), - tags=[i.annotation_tag for i in get_annotation_tags_for_draft(draft)], - state=get_state_for_draft(draft), - shepherd=draft.shepherd if draft.shepherd_id else None, - ) diff --git a/ietf/ietfworkflows/views.py b/ietf/ietfworkflows/views.py deleted file mode 100644 index 75ad58700..000000000 --- a/ietf/ietfworkflows/views.py +++ /dev/null @@ -1,150 +0,0 @@ -from django.http import HttpResponseRedirect, HttpResponseForbidden -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext -from django.conf import settings -from django.core.urlresolvers import reverse as urlreverse - -from ietf.idtracker.models import InternetDraft -from ietf.ietfworkflows.models import Stream, StreamDelegate -from ietf.ietfworkflows.forms import (DraftTagsStateForm, - NoWorkflowStateForm, - StreamDelegatesForm) -from ietf.ietfworkflows.streams import (get_stream_from_draft, - get_streamed_draft) -from ietf.ietfworkflows.utils import (get_workflow_history_for_draft, - get_workflow_for_draft, - get_annotation_tags_for_draft, - get_state_for_draft) -from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, - is_chair_of_stream, can_adopt) -from ietf.doc.utils import get_tags_for_stream_id -from ietf.name.models import DocTagName -from ietf.group.utils import save_group_in_history -from ietf.group.models import Group, Role - - -REDUCED_HISTORY_LEN = 20 - - -def stream_history(request, name): - draft = get_object_or_404(InternetDraft, filename=name) - streamed = get_streamed_draft(draft) - stream = get_stream_from_draft(draft) - workflow = get_workflow_for_draft(draft) - tags = [] - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - used = list(draft.tags.all()) - tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id(draft.stream_id)) - for t in tags: - t.setted = t.slug in used - else: - if workflow: - tags_setted = [i.annotation_tag.pk for i in get_annotation_tags_for_draft(draft)] - for tag in workflow.get_tags(): - tag.setted = tag.pk in tags_setted - tags.append(tag) - state = get_state_for_draft(draft) - history = get_workflow_history_for_draft(draft) - show_more = False - if len(history) > REDUCED_HISTORY_LEN: - show_more = True - - return render_to_response('ietfworkflows/stream_history.html', - {'stream': stream, - 'streamed': streamed, - 'draft': draft, - 'tags': tags, - 'state': state, - 'workflow': workflow, - 'show_more': show_more, - 'history': history[:REDUCED_HISTORY_LEN], - }, - context_instance=RequestContext(request)) - - -def _edit_draft_stream(request, draft, form_class=DraftTagsStateForm): - user = request.user - workflow = get_workflow_for_draft(draft) - if not workflow and form_class == DraftTagsStateForm: - form_class = NoWorkflowStateForm - if request.method == 'POST': - form = form_class(user=user, draft=draft, data=request.POST) - form.request = request - if request.POST.get("cancel",""): - return HttpResponseRedirect(draft.get_absolute_url()) - if form.is_valid(): - form.save() - - # This behavior surprises folks. Let's try running awhile without it. - #if form_class == NoWorkflowStateForm and settings.USE_DB_REDESIGN_PROXY_CLASSES: - # return HttpResponseRedirect(urlreverse('ietf.ietfworkflows.views.edit_state', kwargs={ 'name': draft.filename } )) - - return HttpResponseRedirect(draft.get_absolute_url()) - else: - form = form_class(user=user, draft=draft) - form.request = request - state = get_state_for_draft(draft) - stream = get_stream_from_draft(draft) - history = get_workflow_history_for_draft(draft, 'objectworkflowhistoryentry') - tags = get_annotation_tags_for_draft(draft) - milestones = draft.groupmilestone_set.all() - return render_to_response('ietfworkflows/state_edit.html', - {'draft': draft, - 'state': state, - 'stream': stream, - 'workflow': workflow, - 'history': history, - 'tags': tags, - 'form': form, - 'milestones': milestones, - }, - context_instance=RequestContext(request)) - -# these three views are reusing the same view really, which apart from -# being somewhat obscure means that there are subtle bugs (like the -# title being wrong) - would probably be better to switch to a model -# where each part is edited on its own, we come from an overview page -# anyway, so there's not a big win in putting in a common -# overview/edit page here -def edit_adopt(request, name): - draft = get_object_or_404(InternetDraft, filename=name) - if not can_adopt(request.user, draft): - return HttpResponseForbidden("You don't have permission to access this view") - return _edit_draft_stream(request, draft, NoWorkflowStateForm) - -def edit_state(request, name): - draft = get_object_or_404(InternetDraft, filename=name, stream__isnull=False) - if not can_edit_state(request.user, draft, ): - return HttpResponseForbidden("You don't have permission to access this view") - return _edit_draft_stream(request, draft, DraftTagsStateForm) - - - -def stream_delegates(request, stream_name): - stream = get_object_or_404(Stream, name=stream_name) - if not is_chair_of_stream(request.user, stream): - return HttpResponseForbidden('You have no permission to access this view') - chairs = stream.get_chairs() - form = StreamDelegatesForm(stream=stream) - if request.method == 'POST': - if request.POST.get('delete', False): - pk_list = request.POST.getlist('remove_delegate') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - stream_group = Group.objects.get(acronym=stream.slug) - save_group_in_history(stream_group) - Role.objects.filter(person__in=pk_list, group=stream_group, name="delegate").delete() - else: - StreamDelegate.objects.filter(stream=stream, person__pk__in=pk_list).delete() - else: - form = StreamDelegatesForm(request.POST, stream=stream) - if form.is_valid(): - form.save() - form = StreamDelegatesForm(stream=stream) - delegates = stream.get_delegates() - return render_to_response('ietfworkflows/stream_delegates.html', - {'stream': stream, - 'chairs': chairs, - 'delegates': delegates, - 'form': form, - }, - context_instance=RequestContext(request)) diff --git a/ietf/settings.py b/ietf/settings.py index 4d9c29d29..0d24d4a95 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -175,7 +175,6 @@ INSTALLED_APPS = ( 'ietf.idrfc', 'ietf.wginfo', 'ietf.submit', - 'ietf.ietfworkflows', 'ietf.wgcharter', 'ietf.sync', 'ietf.community', diff --git a/ietf/templates/ietfworkflows/annotation_tags_updated_mail.txt b/ietf/templates/ietfworkflows/annotation_tags_updated_mail.txt deleted file mode 100644 index dafe2376b..000000000 --- a/ietf/templates/ietfworkflows/annotation_tags_updated_mail.txt +++ /dev/null @@ -1,11 +0,0 @@ -{% autoescape off %} -The annotation tags of document {{ doc }} have been updated. See more information below. - -Annotation tags set: {{ entry.setted }} -Annotation tags reset: {{ entry.unsetted }} -Date of the change: {{ entry.change_date }} -Author of the change: {{ entry.person }} - -Comment: -{{ entry.comment }} -{% endautoescape %} diff --git a/ietf/templates/ietfworkflows/edit_actions.html b/ietf/templates/ietfworkflows/edit_actions.html deleted file mode 100644 index 214905233..000000000 --- a/ietf/templates/ietfworkflows/edit_actions.html +++ /dev/null @@ -1,6 +0,0 @@ -
      - {% for action_name, url in actions %} - {% if not forloop.first %} | {% endif%} {{ action_name }} - {% endfor %} -
      - diff --git a/ietf/templates/ietfworkflows/noworkflow_state_form.html b/ietf/templates/ietfworkflows/noworkflow_state_form.html deleted file mode 100644 index d5e3f0372..000000000 --- a/ietf/templates/ietfworkflows/noworkflow_state_form.html +++ /dev/null @@ -1,23 +0,0 @@ -
      - - - - - -
      Adopt this draft in your group
      -
      - {{ form.comment.errors }} - Comment:
      - -
      -
      - {{ form.weeks.errors }} - Estimated time in 'Call for Adoption by WG/RG Issued': (in weeks) -
      -
      - Adopt in Group: {{ form.group }} - {{ form.group.errors }} -
      - -
      -
      diff --git a/ietf/templates/ietfworkflows/state_edit.html b/ietf/templates/ietfworkflows/state_edit.html deleted file mode 100644 index 5cef5eb00..000000000 --- a/ietf/templates/ietfworkflows/state_edit.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "base.html" %} -{% load ietf_streams %} - -{% block morecss %} -table.state-history p { margin: 0px; } -table.edit-form ul { padding: 0px; list-style-type: none; margin: 0px; margin-bottom: 2em; } -table.edit-form ul li, table.edit-form div.free-change { padding: 0px 2em; } -table.edit-form ul li.evenrow { background-color: #edf5ff; } -table.edit-form textarea { width: 95%; height: 120px; } -table.edit-form span.required { color: red; } -table.edit-form ul.errorlist { border-width: 0px; padding: 0px; margin: 0px;} -table.edit-form ul.errorlist li { color: red; margin: 0px; padding: 0px;} -table.edit-form div.field { margin: 1em 0px; } -table.edit-form div.submit-row { margin: 0px 2em; } -table.edit-form div.error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; } -table.edit-form-tags tr { vertical-align: top; } -table.edit-form-tags textarea { height: 200px; } -table.edit-form-tags ul { border-width: 0px; padding: 0px 2em; } -table.edit-form-tags ul li { padding: 0px; } - -#new-edit-form { clear:both; padding-top: 1em; } -#new-edit-form th { padding-left:0; max-width: 8em;} -#new-edit-form #id_comment { width: 30em; height: 5em; } -#new-edit-form #id_weeks { width: 2em;} -#new-edit-form ul {list-style-type: none; padding-left:0; margin-top:0; margin-bottom:0; } -.rec-state-header { float:left; padding-left:2px; padding-right:.5em; } -.rec-statelist {list-style-type: none; padding-left:0; margin-top:0; margin-bottom:0; } -.rec-state { float:left; padding-right:.5em; } - -{% endblock morecss %} - -{% block title %}Change state for {{ draft }}{% endblock %} - -{% block content %} -

      Change state for {{ draft }}

      - -{% if state and state.slug == "wg-doc" and not milestones %} -

      This document is not part of any milestone. You may wish to add it to one.

      -{% endif %} - -{% if state and state.slug == "sub-pub" and milestones %} -

      This document is part of {% if milestones|length > 1 %}{{ milestones|length }} -milestones{% else %}a milestone{% endif %}. Now that the draft is -submitted to IESG for publication, you may wish to -update the -milestone{{ milestones|pluralize }}.

      -{% endif %} - -{{ form }} - -{% endblock %} diff --git a/ietf/templates/ietfworkflows/state_form.html b/ietf/templates/ietfworkflows/state_form.html deleted file mode 100644 index a96b20f3e..000000000 --- a/ietf/templates/ietfworkflows/state_form.html +++ /dev/null @@ -1,32 +0,0 @@ -
      - -
      - {% with form.get_next_states as next_states %} - {% if next_states %} - Recommended next state{{next_states|pluralize}}: -
        - {% for state in next_states %} -
      • '{{ state.name }}'{% if not forloop.last %}, {% endif %}
      • - {% endfor %} -
      - {% endif %} - {% endwith %} -
      - - - {% for field in form.visible_fields %} - - - - - {% endfor %} -
      {{ field.label_tag }}:{{ field }} - {% if field.help_text %}
      {{ field.help_text }}
      {% endif %} - - {{ field.errors }} -
      -
      - - -
      -
      diff --git a/ietf/templates/ietfworkflows/state_updated_mail.txt b/ietf/templates/ietfworkflows/state_updated_mail.txt deleted file mode 100644 index 074b93431..000000000 --- a/ietf/templates/ietfworkflows/state_updated_mail.txt +++ /dev/null @@ -1,11 +0,0 @@ -{% autoescape off %} -The state of document {{ doc }} has been updated. See more information below. - -Previous state: {{ entry.from_state }} -Current state: {{ entry.to_state }} -Transition date: {{ entry.transition_date }} -Author of the change: {{ entry.person }} - -Comment: -{{ entry.comment }} -{% endautoescape %} diff --git a/ietf/templates/ietfworkflows/stream_delegates.html b/ietf/templates/ietfworkflows/stream_delegates.html deleted file mode 100644 index ca77c9575..000000000 --- a/ietf/templates/ietfworkflows/stream_delegates.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "base.html" %} -{% load ietf_streams ietf_filters %} - -{% block title %}Manage delegates for {{ stream.name }} stream{% endblock %} - -{% block content %} -

      Stream: {{ stream.name }}

      - -{% if chairs %} -

      {{ stream.name }} stream chairs

      - - -{% for chair in chairs %} - -{% endfor %} -
      NameEmail
      {{ chair }}{{ chair.email.0 }} <{{ chair.email.1 }}>
      -{% endif %} - -

      {{ stream.name }} stream delegates

      -
      -{% if delegates %} - - -{% for delegate in delegates %} - -{% endfor %} -
      NameEmail
      {{ delegate }}{{ delegate.email.0 }} <{{ delegate.email.1 }}>
      -

      - -

      -{% else %} -

      -No delegates for {{ stream.name }} stream assigned. -

      -{% endif %} -

      -Enter a valid e-mail address to add a new delegate. -

      -{% if form.errors.email %}{{ form.errors.email }}{% endif %} -{{ form.email }} - -
      -{% endblock %} diff --git a/ietf/templates/ietfworkflows/stream_form.html b/ietf/templates/ietfworkflows/stream_form.html deleted file mode 100644 index 268f9ecef..000000000 --- a/ietf/templates/ietfworkflows/stream_form.html +++ /dev/null @@ -1,22 +0,0 @@ -
      - - - - - - - - -
      Select the new stream
      -
      - {{ form.comment.errors }} - Comment: *
      - -
      -
      -
      - {{ form.stream.errors }} - {{ form.stream }} -
      -
      -
      diff --git a/ietf/templates/ietfworkflows/stream_history.html b/ietf/templates/ietfworkflows/stream_history.html deleted file mode 100644 index ae4fb7b19..000000000 --- a/ietf/templates/ietfworkflows/stream_history.html +++ /dev/null @@ -1,44 +0,0 @@ -{% load ietf_streams %} - - - - - -
      - {% if workflow %} -
        - {% for tag in tags %} - {{ tag }} - {% endfor %} -
      - {% endif %} -
      -
      - {% if stream %} -

      - {{ stream|default:"Without stream" }}{% if stream.with_groups and streamed.group %} :: {{ streamed.group|default:"" }}{% endif %} -

      -

      Current state: {{ state|default:"None" }}

      - {% else %} -

      Without stream

      - {% endif %} -
      - - {% if history %} -
      - {% if show_more %} -

      Viewing the last 20 entries. Show full log.

      - {% endif %} - {% for entry in history %} - {% workflow_history_entry entry %} - {% endfor %} - {% if show_more %} -

      Viewing the last 20 entries. Show full log.

      - {% endif %} -
      - {% else %} -

      - There is no stream history for this document. -

      - {% endif %} -
      diff --git a/ietf/templates/ietfworkflows/stream_state.html b/ietf/templates/ietfworkflows/stream_state.html deleted file mode 100644 index d06c4ac1e..000000000 --- a/ietf/templates/ietfworkflows/stream_state.html +++ /dev/null @@ -1,11 +0,0 @@ -{% if draft %} -
      -
      -{% if stream %} -{% if state %}{{ state.name }}{% else %}{{ stream }}{% endif %} -{% if milestones %}{% for m in milestones %}{{ m.due|date:"M Y" }}{% endfor %}{% endif %} -{% else %} - No stream assigned -{% endif %} -
      -{% endif %} diff --git a/ietf/templates/ietfworkflows/stream_state_redesign.html b/ietf/templates/ietfworkflows/stream_state_redesign.html deleted file mode 100644 index 0c334f865..000000000 --- a/ietf/templates/ietfworkflows/stream_state_redesign.html +++ /dev/null @@ -1,10 +0,0 @@ -{% if doc %} -
      -
      -{% if stream %} -{% if state %}{{ state.name }}{% else %}{{ stream }}{% endif %} -{% else %} - No stream assigned -{% endif %} -
      -{% endif %} diff --git a/ietf/templates/ietfworkflows/stream_updated_mail.txt b/ietf/templates/ietfworkflows/stream_updated_mail.txt deleted file mode 100644 index b98640b88..000000000 --- a/ietf/templates/ietfworkflows/stream_updated_mail.txt +++ /dev/null @@ -1,11 +0,0 @@ -{% autoescape off %} -The stream of document {{ doc }} has been updated. See more information below. - -Previous stream: {{ entry.from_stream }} -Current stream: {{ entry.to_stream }} -Transition date: {{ entry.transition_date }} -Author of the change: {{ entry.person }} - -Comment: -{{ entry.comment }} -{% endautoescape %} diff --git a/ietf/templates/ietfworkflows/tags_form.html b/ietf/templates/ietfworkflows/tags_form.html deleted file mode 100644 index e3388a868..000000000 --- a/ietf/templates/ietfworkflows/tags_form.html +++ /dev/null @@ -1,22 +0,0 @@ -
      - - - - - - -
      1. Input information about change2. Select annotation tags
      -
      - {{ form.errors.comment }} - Comment: *
      - -
      -
      -
      - {{ form.tags }} -
      -
      - -
      -
      -
      diff --git a/ietf/templates/ietfworkflows/workflow_history_entry.html b/ietf/templates/ietfworkflows/workflow_history_entry.html deleted file mode 100644 index 3f30673f2..000000000 --- a/ietf/templates/ietfworkflows/workflow_history_entry.html +++ /dev/null @@ -1,12 +0,0 @@ -
      -
      - - {{ entry.person }} -
      -
      - {{ entry.describe_change|safe|linebreaks }} -
      -
      - {{ entry.comment }} -
      - From e5871284b645cbb2f8e3cf5ee2719b5eccc4df8e Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sat, 28 Sep 2013 19:50:56 +0000 Subject: [PATCH 043/173] Remove ietfworkflows from mkdiagram - Legacy-Id: 6298 --- bin/mkdiagram | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/mkdiagram b/bin/mkdiagram index 5eca0efc1..ef50ce673 100755 --- a/bin/mkdiagram +++ b/bin/mkdiagram @@ -9,7 +9,7 @@ trap 'echo "$program($LINENO): Command failed with error code $? ($0 $*)"; exit if [ "$*" ]; then apps="$@"; graph="${1%.*}"; else apps=$(ls */models.py | sed 's!/models.py!!'); graph="models"; fi newapps="doc group meeting message person name" -legacyapps="announcements idindex idrfc idtracker iesg ietfauth ietfworkflows ipr liaisons mailinglists proceedings redirects submit wgcharter wginfo" +legacyapps="announcements idindex idrfc idtracker iesg ietfauth ipr liaisons mailinglists proceedings redirects submit wgcharter wginfo" proxy="$(grep ^class */proxy.py | tr '()' ' ' | awk '{printf $2 ","}')" names="$(grep ^class name/models.py | tr '()' ' ' | awk '{printf $2 ","}')" From f7ad84ecb905af72a02ab5c4e9f5cd80ffbebcb6 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sat, 28 Sep 2013 19:52:28 +0000 Subject: [PATCH 044/173] Remove (external) workflows app which has been obsolete since the move to the new schema - Legacy-Id: 6299 --- ietf/settings.py | 1 - workflows/.gitignore | 1 - workflows/__init__.py | 61 -- workflows/locale/de/LC_MESSAGES/django.mo | Bin 798 -> 0 bytes workflows/locale/de/LC_MESSAGES/django.po | 60 -- workflows/migrations/.gitignore | 1 - workflows/migrations/0001_initial.py | 225 ------- workflows/migrations/__init__.py | 0 workflows/models.py | 357 ----------- .../templates/workflows/transitions.html | 10 - workflows/templatetags/.gitignore | 1 - workflows/templatetags/__init__.py | 0 workflows/templatetags/workflows_tags.py | 18 - workflows/tests.py | 600 ------------------ workflows/urls.py | 7 - workflows/utils.py | 351 ---------- workflows/views.py | 0 17 files changed, 1693 deletions(-) delete mode 100644 workflows/.gitignore delete mode 100644 workflows/__init__.py delete mode 100644 workflows/locale/de/LC_MESSAGES/django.mo delete mode 100644 workflows/locale/de/LC_MESSAGES/django.po delete mode 100644 workflows/migrations/.gitignore delete mode 100644 workflows/migrations/0001_initial.py delete mode 100644 workflows/migrations/__init__.py delete mode 100644 workflows/models.py delete mode 100644 workflows/templates/workflows/transitions.html delete mode 100644 workflows/templatetags/.gitignore delete mode 100644 workflows/templatetags/__init__.py delete mode 100644 workflows/templatetags/workflows_tags.py delete mode 100644 workflows/tests.py delete mode 100644 workflows/urls.py delete mode 100644 workflows/utils.py delete mode 100644 workflows/views.py diff --git a/ietf/settings.py b/ietf/settings.py index 0d24d4a95..35b28b2c7 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -154,7 +154,6 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'django.contrib.messages', 'south', - 'workflows', 'permissions', 'ietf.person', 'ietf.name', diff --git a/workflows/.gitignore b/workflows/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/workflows/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/workflows/__init__.py b/workflows/__init__.py deleted file mode 100644 index 271167dbe..000000000 --- a/workflows/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# workflows imports -import workflows.utils - -class WorkflowBase(object): - """Mixin class to make objects workflow aware. - """ - def get_workflow(self): - """Returns the current workflow of the object. - """ - return workflows.utils.get_workflow(self) - - def remove_workflow(self): - """Removes the workflow from the object. After this function has been - called the object has no *own* workflow anymore (it might have one via - its content type). - - """ - return workflows.utils.remove_workflow_from_object(self) - - def set_workflow(self, workflow): - """Sets the passed workflow to the object. This will set the local - workflow for the object. - - If the object has already the given workflow nothing happens. - Otherwise the object gets the passed workflow and the state is set to - the workflow's initial state. - - **Parameters:** - - workflow - The workflow which should be set to the object. Can be a Workflow - instance or a string with the workflow name. - obj - The object which gets the passed workflow. - """ - return workflows.utils.set_workflow_for_object(workflow) - - def get_state(self): - """Returns the current workflow state of the object. - """ - return workflows.utils.get_state(self) - - def set_state(self, state): - """Sets the workflow state of the object. - """ - return workflows.utils.set_state(self, state) - - def set_initial_state(self): - """Sets the initial state of the current workflow to the object. - """ - return self.set_state(self.get_workflow().initial_state) - - def get_allowed_transitions(self, user): - """Returns allowed transitions for the current state. - """ - return workflows.utils.get_allowed_transitions(self, user) - - def do_transition(self, transition, user): - """Processes the passed transition (if allowed). - """ - return workflows.utils.do_transition(self, transition, user) \ No newline at end of file diff --git a/workflows/locale/de/LC_MESSAGES/django.mo b/workflows/locale/de/LC_MESSAGES/django.mo deleted file mode 100644 index e4512512fa1dfb4c2aff568f0b09cb7566a1b859..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 798 zcmY+BzmL-}6vqveUjZbznZeAJ@6rmP61dZaCU-<6ZIz}VPAqQgriRohw#$LU#>&dZ z$e+P>5nj znm`BGsq-%I3giP|1Nf|7=XG8H&%^vnAl4lNF9F{HcR=q!=>PfbU*9h^zt%hjqVM;b zKY-}>v*s`01;~E`(Z6wykZZu}K(MX?*Vln(;r-WvZ^HXr1mS+Lsr>Owupp#cs;RWH zR5-FiS(?3BicKP?o1w+|Ko~0(uQv4fLXd&b3uz1*q-7z<&~htCrnxeetszfJ{bE*> z$7G<(=VD@6G-Z#4#w{LgZM&2g%Q<&au`IP^iOJ=ZJy_+2Wu-@*K|f<%Evge)0Nx&L zyDgWwx0&0f?p?2Sr|Gs`7YbP_UP-jA>z;;w%91_{eEP8OB~kOJ-wU0X8_TNu72KBE zqX(lnraeCi>HRSAqj=vBf;1cs_n=heE1nCM3BK@X?Dq~w{vp)G@qQtNjj)QZ=3XIXHm?$ODdYa{ILC_7-Ce|7j*Goe|iCgoJB+@qbbv<`W+X-%Z& zys!p3K, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: 1.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-04-02 09:16+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: models.py:98 models.py:199 models.py:237 -msgid "Name" -msgstr "Name" - -#: models.py:200 models.py:238 models.py:285 models.py:307 -msgid "Workflow" -msgstr "Arbeitsablauf" - -#: models.py:201 -msgid "Transitions" -msgstr "Übergänge" - -#: models.py:239 -msgid "Destination" -msgstr "Ziel" - -#: models.py:240 -msgid "Condition" -msgstr "Kondition" - -#: models.py:241 models.py:350 models.py:373 -msgid "Permission" -msgstr "Recht" - -#: models.py:258 models.py:282 -msgid "Content type" -msgstr "Inhaltstyp" - -#: models.py:259 models.py:283 -msgid "Content id" -msgstr "Inhalts-ID" - -#: models.py:261 models.py:349 models.py:372 -msgid "State" -msgstr "Status" - -#: models.py:306 -msgid "Content Type" -msgstr "Inhaltstyp" - -#: models.py:374 -msgid "Role" -msgstr "Rolle" diff --git a/workflows/migrations/.gitignore b/workflows/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/workflows/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/workflows/migrations/0001_initial.py b/workflows/migrations/0001_initial.py deleted file mode 100644 index d12f59ff3..000000000 --- a/workflows/migrations/0001_initial.py +++ /dev/null @@ -1,225 +0,0 @@ - -from south.db import db -from django.db import models -from workflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'Workflow' - db.create_table('workflows_workflow', ( - ('id', orm['workflows.Workflow:id']), - ('name', orm['workflows.Workflow:name']), - ('initial_state', orm['workflows.Workflow:initial_state']), - )) - db.send_create_signal('workflows', ['Workflow']) - - # Adding model 'StatePermissionRelation' - db.create_table('workflows_statepermissionrelation', ( - ('id', orm['workflows.StatePermissionRelation:id']), - ('state', orm['workflows.StatePermissionRelation:state']), - ('permission', orm['workflows.StatePermissionRelation:permission']), - ('role', orm['workflows.StatePermissionRelation:role']), - )) - db.send_create_signal('workflows', ['StatePermissionRelation']) - - # Adding model 'StateInheritanceBlock' - db.create_table('workflows_stateinheritanceblock', ( - ('id', orm['workflows.StateInheritanceBlock:id']), - ('state', orm['workflows.StateInheritanceBlock:state']), - ('permission', orm['workflows.StateInheritanceBlock:permission']), - )) - db.send_create_signal('workflows', ['StateInheritanceBlock']) - - # Adding model 'WorkflowModelRelation' - db.create_table('workflows_workflowmodelrelation', ( - ('id', orm['workflows.WorkflowModelRelation:id']), - ('content_type', orm['workflows.WorkflowModelRelation:content_type']), - ('workflow', orm['workflows.WorkflowModelRelation:workflow']), - )) - db.send_create_signal('workflows', ['WorkflowModelRelation']) - - # Adding model 'WorkflowPermissionRelation' - db.create_table('workflows_workflowpermissionrelation', ( - ('id', orm['workflows.WorkflowPermissionRelation:id']), - ('workflow', orm['workflows.WorkflowPermissionRelation:workflow']), - ('permission', orm['workflows.WorkflowPermissionRelation:permission']), - )) - db.send_create_signal('workflows', ['WorkflowPermissionRelation']) - - # Adding model 'State' - db.create_table('workflows_state', ( - ('id', orm['workflows.State:id']), - ('name', orm['workflows.State:name']), - ('workflow', orm['workflows.State:workflow']), - )) - db.send_create_signal('workflows', ['State']) - - # Adding model 'Transition' - db.create_table('workflows_transition', ( - ('id', orm['workflows.Transition:id']), - ('name', orm['workflows.Transition:name']), - ('workflow', orm['workflows.Transition:workflow']), - ('destination', orm['workflows.Transition:destination']), - ('condition', orm['workflows.Transition:condition']), - ('permission', orm['workflows.Transition:permission']), - )) - db.send_create_signal('workflows', ['Transition']) - - # Adding model 'WorkflowObjectRelation' - db.create_table('workflows_workflowobjectrelation', ( - ('id', orm['workflows.WorkflowObjectRelation:id']), - ('content_type', orm['workflows.WorkflowObjectRelation:content_type']), - ('content_id', orm['workflows.WorkflowObjectRelation:content_id']), - ('workflow', orm['workflows.WorkflowObjectRelation:workflow']), - )) - db.send_create_signal('workflows', ['WorkflowObjectRelation']) - - # Adding model 'StateObjectRelation' - db.create_table('workflows_stateobjectrelation', ( - ('id', orm['workflows.StateObjectRelation:id']), - ('content_type', orm['workflows.StateObjectRelation:content_type']), - ('content_id', orm['workflows.StateObjectRelation:content_id']), - ('state', orm['workflows.StateObjectRelation:state']), - )) - db.send_create_signal('workflows', ['StateObjectRelation']) - - # Adding ManyToManyField 'State.transitions' - db.create_table('workflows_state_transitions', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('state', models.ForeignKey(orm.State, null=False)), - ('transition', models.ForeignKey(orm.Transition, null=False)) - )) - - # Creating unique_together for [content_type, content_id] on WorkflowObjectRelation. - db.create_unique('workflows_workflowobjectrelation', ['content_type_id', 'content_id']) - - # Creating unique_together for [content_type, content_id, state] on StateObjectRelation. - db.create_unique('workflows_stateobjectrelation', ['content_type_id', 'content_id', 'state_id']) - - # Creating unique_together for [workflow, permission] on WorkflowPermissionRelation. - db.create_unique('workflows_workflowpermissionrelation', ['workflow_id', 'permission_id']) - - - - def backwards(self, orm): - - # Deleting unique_together for [workflow, permission] on WorkflowPermissionRelation. - db.delete_unique('workflows_workflowpermissionrelation', ['workflow_id', 'permission_id']) - - # Deleting unique_together for [content_type, content_id, state] on StateObjectRelation. - db.delete_unique('workflows_stateobjectrelation', ['content_type_id', 'content_id', 'state_id']) - - # Deleting unique_together for [content_type, content_id] on WorkflowObjectRelation. - db.delete_unique('workflows_workflowobjectrelation', ['content_type_id', 'content_id']) - - # Deleting model 'Workflow' - db.delete_table('workflows_workflow') - - # Deleting model 'StatePermissionRelation' - db.delete_table('workflows_statepermissionrelation') - - # Deleting model 'StateInheritanceBlock' - db.delete_table('workflows_stateinheritanceblock') - - # Deleting model 'WorkflowModelRelation' - db.delete_table('workflows_workflowmodelrelation') - - # Deleting model 'WorkflowPermissionRelation' - db.delete_table('workflows_workflowpermissionrelation') - - # Deleting model 'State' - db.delete_table('workflows_state') - - # Deleting model 'Transition' - db.delete_table('workflows_transition') - - # Deleting model 'WorkflowObjectRelation' - db.delete_table('workflows_workflowobjectrelation') - - # Deleting model 'StateObjectRelation' - db.delete_table('workflows_stateobjectrelation') - - # Dropping ManyToManyField 'State.transitions' - db.delete_table('workflows_state_transitions') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'permissions.role': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateinheritanceblock': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.statepermissionrelation': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - }, - 'workflows.workflowmodelrelation': { - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wmrs'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wors'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowpermissionrelation': { - 'Meta': {'unique_together': "(('workflow', 'permission'),)"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': "orm['permissions.Permission']"}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"}) - } - } - - complete_apps = ['workflows'] diff --git a/workflows/migrations/__init__.py b/workflows/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/workflows/models.py b/workflows/models.py deleted file mode 100644 index d7b5ac68d..000000000 --- a/workflows/models.py +++ /dev/null @@ -1,357 +0,0 @@ -from django.db import models - -# django imports -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -from django.utils.translation import ugettext_lazy as _ - -# permissions imports -import permissions.utils -from permissions.models import Permission -from permissions.models import Role - -class Workflow(models.Model): - """A workflow consists of a sequence of connected (through transitions) - states. It can be assigned to a model and / or model instances. If a - model instance has a workflow it takes precendence over the model's - workflow. - - **Attributes:** - - model - The model the workflow belongs to. Can be any - - content - The object the workflow belongs to. - - name - The unique name of the workflow. - - states - The states of the workflow. - - initial_state - The initial state the model / content gets if created. - - """ - name = models.CharField(_(u"Name"), max_length=100, unique=True) - initial_state = models.ForeignKey("State", related_name="workflow_state", blank=True, null=True) - permissions = models.ManyToManyField(Permission, symmetrical=False, through="WorkflowPermissionRelation") - - def __unicode__(self): - return self.name - - def get_initial_state(self): - """Returns the initial state of the workflow. Takes the first one if - no state has been defined. - """ - if self.initial_state: - return self.initial_state - else: - try: - return self.states.all()[0] - except IndexError: - return None - - def get_objects(self): - """Returns all objects which have this workflow assigned. Globally - (via the object's content type) or locally (via the object itself). - """ - import workflows.utils - objs = [] - - # Get all objects whose content type has this workflow - for wmr in WorkflowModelRelation.objects.filter(workflow=self): - ctype = wmr.content_type - # We have also to check whether the global workflow is not - # overwritten. - for obj in ctype.model_class().objects.all(): - if workflows.utils.get_workflow(obj) == self: - objs.append(obj) - - # Get all objects whose local workflow this workflow - for wor in WorkflowObjectRelation.objects.filter(workflow=self): - if wor.content not in objs: - objs.append(wor.content) - - return objs - - def set_to(self, ctype_or_obj): - """Sets the workflow to passed content type or object. See the specific - methods for more information. - - **Parameters:** - - ctype_or_obj - The content type or the object to which the workflow should be set. - Can be either a ContentType instance or any Django model instance. - """ - if isinstance(ctype_or_obj, ContentType): - return self.set_to_model(ctype_or_obj) - else: - return self.set_to_object(ctype_or_obj) - - def set_to_model(self, ctype): - """Sets the workflow to the passed content type. If the content - type has already an assigned workflow the workflow is overwritten. - - **Parameters:** - - ctype - The content type which gets the workflow. Can be any Django model - instance. - """ - try: - wor = WorkflowModelRelation.objects.get(content_type=ctype) - except WorkflowModelRelation.DoesNotExist: - WorkflowModelRelation.objects.create(content_type=ctype, workflow=self) - else: - wor.workflow = self - wor.save() - - def set_to_object(self, obj): - """Sets the workflow to the passed object. - - If the object has already the given workflow nothing happens. Otherwise - the workflow is set to the objectthe state is set to the workflow's - initial state. - - **Parameters:** - - obj - The object which gets the workflow. - """ - import workflows.utils - - ctype = ContentType.objects.get_for_model(obj) - try: - wor = WorkflowObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - except WorkflowObjectRelation.DoesNotExist: - WorkflowObjectRelation.objects.create(content = obj, workflow=self) - workflows.utils.set_state(obj, self.initial_state) - else: - if wor.workflow != self: - wor.workflow = self - wor.save() - workflows.utils.set_state(self.initial_state) - -class State(models.Model): - """A certain state within workflow. - - **Attributes:** - - name - The unique name of the state within the workflow. - - workflow - The workflow to which the state belongs. - - transitions - The transitions of a workflow state. - - """ - name = models.CharField(_(u"Name"), max_length=100) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="states") - transitions = models.ManyToManyField("Transition", verbose_name=_(u"Transitions"), blank=True, null=True, related_name="states") - - class Meta: - ordering = ("name", ) - - def __unicode__(self): - return "%s (%s)" % (self.name, self.workflow.name) - - def get_allowed_transitions(self, obj, user): - """Returns all allowed transitions for passed object and user. - """ - transitions = [] - for transition in self.transitions.all(): - permission = transition.permission - if permission is None: - transitions.append(transition) - else: - # First we try to get the objects specific has_permission - # method (in case the object inherits from the PermissionBase - # class). - try: - if obj.has_permission(user, permission.codename): - transitions.append(transition) - except AttributeError: - if permissions.utils.has_permission(obj, user, permission.codename): - transitions.append(transition) - return transitions - -class Transition(models.Model): - """A transition from a source to a destination state. The transition can - be used from several source states. - - **Attributes:** - - name - The unique name of the transition within a workflow. - - workflow - The workflow to which the transition belongs. Must be a Workflow - instance. - - destination - The state after a transition has been processed. Must be a State - instance. - - condition - The condition when the transition is available. Can be any python - expression. - - permission - The necessary permission to process the transition. Must be a - Permission instance. - - """ - name = models.CharField(_(u"Name"), max_length=100) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="transitions") - destination = models.ForeignKey(State, verbose_name=_(u"Destination"), null=True, blank=True, related_name="destination_state") - condition = models.CharField(_(u"Condition"), blank=True, max_length=100) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"), blank=True, null=True) - - def __unicode__(self): - return self.name - -class StateObjectRelation(models.Model): - """Stores the workflow state of an object. - - Provides a way to give any object a workflow state without changing the - object's model. - - **Attributes:** - - content - The object for which the state is stored. This can be any instance of - a Django model. - - state - The state of content. This must be a State instance. - """ - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="state_object", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - state = models.ForeignKey(State, verbose_name = _(u"State")) - - def __unicode__(self): - return "%s %s - %s" % (self.content_type.name, self.content_id, self.state.name) - - class Meta: - unique_together = ("content_type", "content_id", "state") - -class WorkflowObjectRelation(models.Model): - """Stores an workflow of an object. - - Provides a way to give any object a workflow without changing the object's - model. - - **Attributes:** - - content - The object for which the workflow is stored. This can be any instance of - a Django model. - - workflow - The workflow which is assigned to an object. This needs to be a workflow - instance. - """ - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="workflow_object", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="wors") - - class Meta: - unique_together = ("content_type", "content_id") - - def __unicode__(self): - return "%s %s - %s" % (self.content_type.name, self.content_id, self.workflow.name) - -class WorkflowModelRelation(models.Model): - """Stores an workflow for a model (ContentType). - - Provides a way to give any object a workflow without changing the model. - - **Attributes:** - - Content Type - The content type for which the workflow is stored. This can be any - instance of a Django model. - - workflow - The workflow which is assigned to an object. This needs to be a - workflow instance. - """ - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content Type"), unique=True) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="wmrs") - - def __unicode__(self): - return "%s - %s" % (self.content_type.name, self.workflow.name) - -# Permissions relation ####################################################### - -class WorkflowPermissionRelation(models.Model): - """Stores the permissions for which a workflow is responsible. - - **Attributes:** - - workflow - The workflow which is responsible for the permissions. Needs to be a - Workflow instance. - - permission - The permission for which the workflow is responsible. Needs to be a - Permission instance. - """ - workflow = models.ForeignKey(Workflow) - permission = models.ForeignKey(Permission, related_name="permissions") - - class Meta: - unique_together = ("workflow", "permission") - - def __unicode__(self): - return "%s %s" % (self.workflow.name, self.permission.name) - -class StateInheritanceBlock(models.Model): - """Stores inheritance block for state and permission. - - **Attributes:** - - state - The state for which the inheritance is blocked. Needs to be a State - instance. - - permission - The permission for which the instance is blocked. Needs to be a - Permission instance. - """ - state = models.ForeignKey(State, verbose_name=_(u"State")) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - - def __unicode__(self): - return "%s %s" % (self.state.name, self.permission.name) - -class StatePermissionRelation(models.Model): - """Stores granted permission for state and role. - - **Attributes:** - - state - The state for which the role has the permission. Needs to be a State - instance. - - permission - The permission for which the workflow is responsible. Needs to be a - Permission instance. - - role - The role for which the state has the permission. Needs to be a lfc - Role instance. - """ - state = models.ForeignKey(State, verbose_name=_(u"State")) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - role = models.ForeignKey(Role, verbose_name=_(u"Role")) - - def __unicode__(self): - return "%s %s %s" % (self.state.name, self.role.name, self.permission.name) diff --git a/workflows/templates/workflows/transitions.html b/workflows/templates/workflows/transitions.html deleted file mode 100644 index 635cbba02..000000000 --- a/workflows/templates/workflows/transitions.html +++ /dev/null @@ -1,10 +0,0 @@ -{% load i18n %} - - diff --git a/workflows/templatetags/.gitignore b/workflows/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/workflows/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/workflows/templatetags/__init__.py b/workflows/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/workflows/templatetags/workflows_tags.py b/workflows/templatetags/workflows_tags.py deleted file mode 100644 index e95357a32..000000000 --- a/workflows/templatetags/workflows_tags.py +++ /dev/null @@ -1,18 +0,0 @@ -# django imports -from django import template - -# workflows imports -import workflows.utils - -register = template.Library() - -@register.inclusion_tag('workflows/transitions.html', takes_context=True) -def transitions(context, obj): - """ - """ - request = context.get("request") - - return { - "transitions" : workflows.utils.get_allowed_transitions(obj, request.user), - "state" : workflows.utils.get_state(obj), - } diff --git a/workflows/tests.py b/workflows/tests.py deleted file mode 100644 index 2fda0ad4b..000000000 --- a/workflows/tests.py +++ /dev/null @@ -1,600 +0,0 @@ -# django imports -from django.contrib.contenttypes.models import ContentType -from django.contrib.flatpages.models import FlatPage -from django.test import TestCase -from django.contrib.auth.models import User -from django.contrib.sessions.backends.file import SessionStore -from django.core.handlers.wsgi import WSGIRequest -from django.test.client import Client - -# workflows import -import permissions.utils -import workflows.utils -from workflows.models import State -from workflows.models import StateInheritanceBlock -from workflows.models import StatePermissionRelation -from workflows.models import StateObjectRelation -from workflows.models import Transition -from workflows.models import Workflow -from workflows.models import WorkflowModelRelation -from workflows.models import WorkflowObjectRelation -from workflows.models import WorkflowPermissionRelation - -class WorkflowTestCase(TestCase): - """Tests a simple workflow without permissions. - """ - def setUp(self): - """ - """ - create_workflow(self) - - def test_get_states(self): - """ - """ - states = self.w.states.all() - self.assertEqual(states[0], self.private) - self.assertEqual(states[1], self.public) - - def test_unicode(self): - """ - """ - self.assertEqual(self.w.__unicode__(), u"Standard") - -class PermissionsTestCase(TestCase): - """Tests a simple workflow with permissions. - """ - def setUp(self): - """ - """ - create_workflow(self) - - # Register roles - self.anonymous = permissions.utils.register_role("Anonymous") - self.owner = permissions.utils.register_role("Owner") - - self.user = User.objects.create(username="john") - permissions.utils.add_role(self.user, self.owner) - - # Example content type - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - - # Registers permissions - self.view = permissions.utils.register_permission("View", "view") - self.edit = permissions.utils.register_permission("Edit", "edit") - - # Add all permissions which are managed by the workflow - wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.view) - wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.edit) - - # Add permissions for single states - spr = StatePermissionRelation.objects.create(state=self.public, permission=self.view, role=self.owner) - spr = StatePermissionRelation.objects.create(state=self.private, permission=self.view, role=self.owner) - spr = StatePermissionRelation.objects.create(state=self.private, permission=self.edit, role=self.owner) - - # Add inheritance block for single states - sib = StateInheritanceBlock.objects.create(state=self.private, permission=self.view) - sib = StateInheritanceBlock.objects.create(state=self.private, permission=self.edit) - sib = StateInheritanceBlock.objects.create(state=self.public, permission=self.edit) - - workflows.utils.set_workflow(self.page_1, self.w) - - def test_set_state(self): - """ - """ - # Permissions - result = permissions.utils.has_permission(self.page_1, self.user, "edit") - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - # Inheritance - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - result = permissions.utils.is_inherited(self.page_1, "edit") - self.assertEqual(result, False) - - # Change state - workflows.utils.set_state(self.page_1, self.public) - - # Permissions - result = permissions.utils.has_permission(self.page_1, self.user, "edit") - self.assertEqual(result, False) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - # Inheritance - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - result = permissions.utils.is_inherited(self.page_1, "edit") - self.assertEqual(result, False) - - def test_set_initial_state(self): - """ - """ - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - workflows.utils.do_transition(self.page_1, self.make_public, self.user) - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.public.name) - - workflows.utils.set_initial_state(self.page_1) - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - def test_do_transition(self): - """ - """ - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - # by transition - workflows.utils.do_transition(self.page_1, self.make_public, self.user) - - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.public.name) - - # by name - workflows.utils.do_transition(self.page_1, "Make private", self.user) - - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - # name which does not exist - result = workflows.utils.do_transition(self.page_1, "Make pending", self.user) - self.assertEqual(result, False) - - wrong = Transition.objects.create(name="Wrong", workflow=self.w, destination = self.public) - - # name which does not exist - result = workflows.utils.do_transition(self.page_1, wrong, self.user) - self.assertEqual(result, False) - -class UtilsTestCase(TestCase): - """Tests various methods of the utils module. - """ - def setUp(self): - """ - """ - create_workflow(self) - self.user = User.objects.create() - - def test_workflow(self): - """ - """ - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - def test_state(self): - """ - """ - result = workflows.utils.get_state(self.user) - self.assertEqual(result, None) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_state(self.user) - self.assertEqual(result, self.w.initial_state) - - def test_set_workflow_1(self): - """Set worklow by object - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - wp = Workflow.objects.create(name="Portal") - - # Set for model - workflows.utils.set_workflow_for_model(ctype, wp) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, wp) - - # Set for object - workflows.utils.set_workflow_for_object(self.user, self.w) - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - # The model still have wp - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - def test_set_workflow_2(self): - """Set worklow by name - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - wp = Workflow.objects.create(name="Portal") - - # Set for model - workflows.utils.set_workflow_for_model(ctype, "Portal") - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, wp) - - # Set for object - workflows.utils.set_workflow_for_object(self.user, "Standard") - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - # The model still have wp - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - # Workflow which does not exist - result = workflows.utils.set_workflow_for_model(ctype, "Wrong") - self.assertEqual(result, False) - - result = workflows.utils.set_workflow_for_object(self.user, "Wrong") - self.assertEqual(result, False) - - def test_get_objects_for_workflow_1(self): - """Workflow is added to object. - """ - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, []) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - def test_get_objects_for_workflow_2(self): - """Workflow is added to content type. - """ - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, []) - - ctype = ContentType.objects.get_for_model(self.user) - workflows.utils.set_workflow(ctype, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - def test_get_objects_for_workflow_3(self): - """Workflow is added to content type and object. - """ - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, []) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - ctype = ContentType.objects.get_for_model(self.user) - workflows.utils.set_workflow(ctype, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - def test_get_objects_for_workflow_4(self): - """Get workflow by name - """ - result = workflows.utils.get_objects_for_workflow("Standard") - self.assertEqual(result, []) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_objects_for_workflow("Standard") - self.assertEqual(result, [self.user]) - - # Workflow which does not exist - result = workflows.utils.get_objects_for_workflow("Wrong") - self.assertEqual(result, []) - - def test_remove_workflow_from_model(self): - """ - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(ctype) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_model(ctype, self.w) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - workflows.utils.remove_workflow_from_model(ctype) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, None) - - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, None) - - def test_remove_workflow_from_object(self): - """ - """ - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_object(self.user, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - result = workflows.utils.remove_workflow_from_object(self.user) - self.assertEqual(result, None) - - def test_remove_workflow_1(self): - """Removes workflow from model - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(ctype) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_model(ctype, self.w) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - workflows.utils.remove_workflow(ctype) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, None) - - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, None) - - def test_remove_workflow_2(self): - """Removes workflow from object - """ - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_object(self.user, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - result = workflows.utils.remove_workflow(self.user) - self.assertEqual(result, None) - - def test_get_allowed_transitions(self): - """Tests get_allowed_transitions method - """ - page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - role_1 = permissions.utils.register_role("Role 1") - permissions.utils.add_role(self.user, role_1) - - view = permissions.utils.register_permission("Publish", "publish") - - transitions = self.private.get_allowed_transitions(page_1, self.user) - self.assertEqual(len(transitions), 1) - - # protect the transition with a permission - self.make_public.permission = view - self.make_public.save() - - # user has no transition - transitions = self.private.get_allowed_transitions(page_1, self.user) - self.assertEqual(len(transitions), 0) - - # grant permission - permissions.utils.grant_permission(page_1, role_1, view) - - # user has transition again - transitions = self.private.get_allowed_transitions(page_1, self.user) - self.assertEqual(len(transitions), 1) - - def test_get_workflow_for_object(self): - """ - """ - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - # Set workflow for a user - workflows.utils.set_workflow_for_object(self.user, self.w) - - # Get workflow for the user - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, self.w) - - # Set workflow for a FlatPage - page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - workflows.utils.set_workflow_for_object(page_1, self.w) - - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, self.w) - - result = workflows.utils.get_workflow_for_object(page_1) - self.assertEqual(result, self.w) - -class StateTestCase(TestCase): - """Tests the State model - """ - def setUp(self): - """ - """ - create_workflow(self) - self.user = User.objects.create() - self.role_1 = permissions.utils.register_role("Role 1") - permissions.utils.add_role(self.user, self.role_1) - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - - def test_unicode(self): - """ - """ - self.assertEqual(self.private.__unicode__(), u"Private (Standard)") - - def test_transitions(self): - """ - """ - transitions = self.public.transitions.all() - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_private) - - transitions = self.private.transitions.all() - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_public) - - def test_get_transitions(self): - """ - """ - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_public) - - transitions = self.public.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_private) - - def test_get_allowed_transitions(self): - """ - """ - self.view = permissions.utils.register_permission("Publish", "publish") - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - - # protect the transition with a permission - self.make_public.permission = self.view - self.make_public.save() - - # user has no transition - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 0) - - # grant permission - permissions.utils.grant_permission(self.page_1, self.role_1, self.view) - - # user has transition again - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - -class TransitionTestCase(TestCase): - """Tests the Transition model - """ - def setUp(self): - """ - """ - create_workflow(self) - - def test_unicode(self): - """ - """ - self.assertEqual(self.make_private.__unicode__(), u"Make private") - -class RelationsTestCase(TestCase): - """Tests various Relations models. - """ - def setUp(self): - """ - """ - create_workflow(self) - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - - def test_unicode(self): - """ - """ - # WorkflowObjectRelation - workflows.utils.set_workflow(self.page_1, self.w) - wor = WorkflowObjectRelation.objects.filter()[0] - self.assertEqual(wor.__unicode__(), "flat page 1 - Standard") - - # StateObjectRelation - workflows.utils.set_state(self.page_1, self.public) - sor = StateObjectRelation.objects.filter()[0] - self.assertEqual(sor.__unicode__(), "flat page 1 - Public") - - # WorkflowModelRelation - ctype = ContentType.objects.get_for_model(self.page_1) - workflows.utils.set_workflow(ctype, self.w) - wmr = WorkflowModelRelation.objects.filter()[0] - self.assertEqual(wmr.__unicode__(), "flat page - Standard") - - # WorkflowPermissionRelation - self.view = permissions.utils.register_permission("View", "view") - wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.view) - self.assertEqual(wpr.__unicode__(), "Standard View") - - # StatePermissionRelation - self.owner = permissions.utils.register_role("Owner") - spr = StatePermissionRelation.objects.create(state=self.public, permission=self.view, role=self.owner) - self.assertEqual(spr.__unicode__(), "Public Owner View") - -# Helpers #################################################################### - -def create_workflow(self): - self.w = Workflow.objects.create(name="Standard") - - self.private = State.objects.create(name="Private", workflow= self.w) - self.public = State.objects.create(name="Public", workflow= self.w) - - self.make_public = Transition.objects.create(name="Make public", workflow=self.w, destination = self.public) - self.make_private = Transition.objects.create(name="Make private", workflow=self.w, destination = self.private) - - self.private.transitions.add(self.make_public) - self.public.transitions.add(self.make_private) - - self.w.initial_state = self.private - self.w.save() - -# Taken from "http://www.djangosnippets.org/snippets/963/" -class RequestFactory(Client): - """ - Class that lets you create mock Request objects for use in testing. - - Usage: - - rf = RequestFactory() - get_request = rf.get('/hello/') - post_request = rf.post('/submit/', {'foo': 'bar'}) - - This class re-uses the django.test.client.Client interface, docs here: - http://www.djangoproject.com/documentation/testing/#the-test-client - - Once you have a request object you can pass it to any view function, - just as if that view had been hooked up using a URLconf. - - """ - def request(self, **request): - """ - Similar to parent class, but returns the request object as soon as it - has created it. - """ - environ = { - 'HTTP_COOKIE': self.cookies, - 'PATH_INFO': '/', - 'QUERY_STRING': '', - 'REQUEST_METHOD': 'GET', - 'SCRIPT_NAME': '', - 'SERVER_NAME': 'testserver', - 'SERVER_PORT': 80, - 'SERVER_PROTOCOL': 'HTTP/1.1', - } - environ.update(self.defaults) - environ.update(request) - return WSGIRequest(environ) - -def create_request(): - """ - """ - rf = RequestFactory() - request = rf.get('/') - request.session = SessionStore() - - user = User() - user.is_superuser = True - user.save() - request.user = user - - return request diff --git a/workflows/urls.py b/workflows/urls.py deleted file mode 100644 index 949c2346c..000000000 --- a/workflows/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.conf.urls.defaults import * - -# URL patterns for django-workflows - -urlpatterns = patterns('django-workflows.views', - # Add url patterns here -) diff --git a/workflows/utils.py b/workflows/utils.py deleted file mode 100644 index ef2d32596..000000000 --- a/workflows/utils.py +++ /dev/null @@ -1,351 +0,0 @@ -# django imports -from django.contrib.contenttypes.models import ContentType - -# workflows imports -from permissions.models import ObjectPermission -from permissions.models import ObjectPermissionInheritanceBlock -from workflows.models import StateInheritanceBlock -from workflows.models import StateObjectRelation -from workflows.models import StatePermissionRelation -from workflows.models import Transition -from workflows.models import Workflow -from workflows.models import WorkflowModelRelation -from workflows.models import WorkflowObjectRelation -from workflows.models import WorkflowPermissionRelation - -# permissions imports -import permissions.utils - -def get_objects_for_workflow(workflow): - """Returns all objects which have passed workflow. - - **Parameters:** - - workflow - The workflow for which the objects are returned. Can be a Workflow - instance or a string with the workflow name. - """ - if not isinstance(workflow, Workflow): - try: - workflow = Workflow.objects.get(name=workflow) - except Workflow.DoesNotExist: - return [] - - return workflow.get_objects() - -def remove_workflow(ctype_or_obj): - """Removes the workflow from the passed content type or object. After this - function has been called the content type or object has no workflow - anymore. - - If ctype_or_obj is an object the workflow is removed from the object not - from the belonging content type. - - If ctype_or_obj is an content type the workflow is removed from the - content type not from instances of the content type (if they have an own - workflow) - - ctype_or_obj - The content type or the object to which the passed workflow should be - set. Can be either a ContentType instance or any LFC Django model - instance. - """ - if isinstance(ctype_or_obj, ContentType): - remove_workflow_from_model(ctype_or_obj) - else: - remove_workflow_from_object(ctype_or_obj) - -def remove_workflow_from_model(ctype): - """Removes the workflow from passed content type. After this function has - been called the content type has no workflow anymore (the instances might - have own ones). - - ctype - The content type from which the passed workflow should be removed. - Must be a ContentType instance. - """ - # First delete all states, inheritance blocks and permissions from ctype's - # instances which have passed workflow. - workflow = get_workflow_for_model(ctype) - for obj in get_objects_for_workflow(workflow): - try: - ctype = ContentType.objects.get_for_model(obj) - sor = StateObjectRelation.objects.get(content_id=obj.pk, content_type=ctype) - except StateObjectRelation.DoesNotExist: - pass - else: - sor.delete() - - # Reset all permissions - permissions.utils.reset(obj) - - try: - wmr = WorkflowModelRelation.objects.get(content_type=ctype) - except WorkflowModelRelation.DoesNotExist: - pass - else: - wmr.delete() - -def remove_workflow_from_object(obj): - """Removes the workflow from the passed object. After this function has - been called the object has no *own* workflow anymore (it might have one - via its content type). - - obj - The object from which the passed workflow should be set. Must be a - Django Model instance. - """ - try: - wor = WorkflowObjectRelation.objects.get(content_type=obj) - except WorkflowObjectRelation.DoesNotExist: - pass - else: - wor.delete() - - # Reset all permissions - permissions.utils.reset(obj) - - # Set initial of object's content types workflow (if there is one) - set_initial_state(obj) - -def set_workflow(ctype_or_obj, workflow): - """Sets the workflow for passed content type or object. See the specific - methods for more information. - - **Parameters:** - - workflow - The workflow which should be set to the object or model. - - ctype_or_obj - The content type or the object to which the passed workflow should be - set. Can be either a ContentType instance or any Django model - instance. - """ - return workflow.set_to(ctype_or_obj) - -def set_workflow_for_object(obj, workflow): - """Sets the passed workflow to the passed object. - - If the object has already the given workflow nothing happens. Otherwise - the object gets the passed workflow and the state is set to the workflow's - initial state. - - **Parameters:** - - workflow - The workflow which should be set to the object. Can be a Workflow - instance or a string with the workflow name. - - obj - The object which gets the passed workflow. - """ - if isinstance(workflow, Workflow) == False: - try: - workflow = Workflow.objects.get(name=workflow) - except Workflow.DoesNotExist: - return False - - workflow.set_to_object(obj) - -def set_workflow_for_model(ctype, workflow): - """Sets the passed workflow to the passed content type. If the content - type has already an assigned workflow the workflow is overwritten. - - The objects which had the old workflow must updated explicitely. - - **Parameters:** - - workflow - The workflow which should be set to passend content type. Must be a - Workflow instance. - - ctype - The content type to which the passed workflow should be assigned. Can - be any Django model instance - """ - if isinstance(workflow, Workflow) == False: - try: - workflow = Workflow.objects.get(name=workflow) - except Workflow.DoesNotExist: - return False - - workflow.set_to_model(ctype) - -def get_workflow(obj): - """Returns the workflow for the passed object. It takes it either from - the passed object or - if the object doesn't have a workflow - from the - passed object's ContentType. - - **Parameters:** - - object - The object for which the workflow should be returend. Can be any - Django model instance. - """ - workflow = get_workflow_for_object(obj) - if workflow is not None: - return workflow - - ctype = ContentType.objects.get_for_model(obj) - return get_workflow_for_model(ctype) - -def get_workflow_for_object(obj): - """Returns the workflow for the passed object. - - **Parameters:** - - obj - The object for which the workflow should be returned. Can be any - Django model instance. - """ - try: - ctype = ContentType.objects.get_for_model(obj) - wor = WorkflowObjectRelation.objects.get(content_id=obj.pk, content_type=ctype) - except WorkflowObjectRelation.DoesNotExist: - return None - else: - return wor.workflow - -def get_workflow_for_model(ctype): - """Returns the workflow for the passed model. - - **Parameters:** - - ctype - The content type for which the workflow should be returned. Must be - a Django ContentType instance. - """ - try: - wor = WorkflowModelRelation.objects.get(content_type=ctype) - except WorkflowModelRelation.DoesNotExist: - return None - else: - return wor.workflow - -def get_state(obj): - """Returns the current workflow state for the passed object. - - **Parameters:** - - obj - The object for which the workflow state should be returned. Can be any - Django model instance. - """ - ctype = ContentType.objects.get_for_model(obj) - try: - sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - except StateObjectRelation.DoesNotExist: - return None - else: - return sor.state - -def set_state(obj, state): - """Sets the state for the passed object to the passed state and updates - the permissions for the object. - - **Parameters:** - - obj - The object for which the workflow state should be set. Can be any - Django model instance. - - state - The state which should be set to the passed object. - """ - if not state: - remove_state(obj) - else: - ctype = ContentType.objects.get_for_model(obj) - try: - sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - except StateObjectRelation.DoesNotExist: - sor = StateObjectRelation.objects.create(content=obj, state=state) - else: - sor.state = state - sor.save() - update_permissions(obj) - -def remove_state(obj): - """Removes the current state for the passed object. - - **Parameters:** - - obj - The object for which the workflow state should be set. Can be any - Django model instance. - - """ - ctype = ContentType.objects.get_for_model(obj) - try: - sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - sor.delete() - except StateObjectRelation.DoesNotExist: - pass - update_permissions(obj) - -def set_initial_state(obj): - """Sets the initial state to the passed object. - """ - wf = get_workflow(obj) - if wf is not None: - set_state(obj, wf.get_initial_state()) - -def get_allowed_transitions(obj, user): - """Returns all allowed transitions for passed object and user. Takes the - current state of the object into account. - - **Parameters:** - - obj - The object for which the transitions should be returned. - - user - The user for which the transitions are allowed. - """ - state = get_state(obj) - if state is None: - return [] - - return state.get_allowed_transitions(obj, user) - -def do_transition(obj, transition, user): - """Processes the passed transition to the passed object (if allowed). - """ - if not isinstance(transition, Transition): - try: - transition = Transition.objects.get(name=transition) - except Transition.DoesNotExist: - return False - - transitions = get_allowed_transitions(obj, user) - if transition in transitions: - set_state(obj, transition.destination) - return True - else: - return False - -def update_permissions(obj): - """Updates the permissions of the passed object according to the object's - current workflow state. - """ - workflow = get_workflow(obj) - state = get_state(obj) - - # Remove all permissions for the workflow - ct = ContentType.objects.get_for_model(obj) - ps = [wpr.permission for wpr in WorkflowPermissionRelation.objects.filter(workflow=workflow)] - - ObjectPermission.objects.filter(content_type = ct, content_id=obj.pk, permission__in=ps).delete() - - # Grant permission for the state - for spr in StatePermissionRelation.objects.filter(state=state): - permissions.utils.grant_permission(obj, spr.role, spr.permission) - - # Remove all inheritance blocks from the object - ObjectPermissionInheritanceBlock.objects.filter( - content_type = ct, content_id=obj.pk, permission__in=ps).delete() - - # Add inheritance blocks of this state to the object - for sib in StateInheritanceBlock.objects.filter(state=state): - permissions.utils.add_inheritance_block(obj, sib.permission) diff --git a/workflows/views.py b/workflows/views.py deleted file mode 100644 index e69de29bb..000000000 From c419271d9771a057e2c7165b41a98e2bbf0d52af Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sat, 28 Sep 2013 20:00:28 +0000 Subject: [PATCH 045/173] Remove (external) permissions app which has been obsolete since the move to the new schema - Legacy-Id: 6300 --- ietf/settings.py | 1 - permissions/.gitignore | 1 - permissions/__init__.py | 148 ---- permissions/backend.py | 45 -- permissions/exceptions.py | 3 - permissions/fixtures/initial.xml | 23 - permissions/locale/de/LC_MESSAGES/django.mo | Bin 701 -> 0 bytes permissions/locale/de/LC_MESSAGES/django.po | 52 -- permissions/migrations/.gitignore | 1 - permissions/migrations/0001_initial.py | 154 ---- permissions/migrations/__init__.py | 0 permissions/models.py | 190 ----- permissions/templatetags/.gitignore | 1 - permissions/templatetags/__init__.py | 0 permissions/templatetags/permissions_tags.py | 48 -- permissions/tests.py | 783 ------------------- permissions/utils.py | 665 ---------------- 17 files changed, 2115 deletions(-) delete mode 100644 permissions/.gitignore delete mode 100644 permissions/__init__.py delete mode 100644 permissions/backend.py delete mode 100644 permissions/exceptions.py delete mode 100644 permissions/fixtures/initial.xml delete mode 100644 permissions/locale/de/LC_MESSAGES/django.mo delete mode 100644 permissions/locale/de/LC_MESSAGES/django.po delete mode 100644 permissions/migrations/.gitignore delete mode 100644 permissions/migrations/0001_initial.py delete mode 100644 permissions/migrations/__init__.py delete mode 100644 permissions/models.py delete mode 100644 permissions/templatetags/.gitignore delete mode 100644 permissions/templatetags/__init__.py delete mode 100644 permissions/templatetags/permissions_tags.py delete mode 100644 permissions/tests.py delete mode 100644 permissions/utils.py diff --git a/ietf/settings.py b/ietf/settings.py index 35b28b2c7..ee09083d9 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -154,7 +154,6 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'django.contrib.messages', 'south', - 'permissions', 'ietf.person', 'ietf.name', 'ietf.group', diff --git a/permissions/.gitignore b/permissions/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/permissions/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/permissions/__init__.py b/permissions/__init__.py deleted file mode 100644 index b81c7766c..000000000 --- a/permissions/__init__.py +++ /dev/null @@ -1,148 +0,0 @@ -import permissions.utils - -class PermissionBase(object): - """Mix-in class for permissions. - """ - def grant_permission(self, role, permission): - """Grants passed permission to passed role. Returns True if the - permission was able to be added, otherwise False. - - **Parameters:** - - role - The role for which the permission should be granted. - - permission - The permission which should be granted. Either a permission - object or the codename of a permission. - """ - return permissions.utils.grant_permission(self, role, permission) - - def remove_permission(self, role, permission): - """Removes passed permission from passed role. Returns True if the - permission has been removed. - - **Parameters:** - - role - The role for which a permission should be removed. - - permission - The permission which should be removed. Either a permission object - or the codename of a permission. - """ - return permissions.utils.remove_permission(self, role, permission) - - def has_permission(self, user, permission, roles=[]): - """Returns True if the passed user has passed permission for this - instance. Otherwise False. - - **Parameters:** - - permission - The permission's codename which should be checked. Must be a - string with a valid codename. - - user - The user for which the permission should be checked. - - roles - If passed, these roles will be assigned to the user temporarily - before the permissions are checked. - """ - return permissions.utils.has_permission(self, user, permission, roles) - - def check_permission(self, user, permission, roles=[]): - """Raise Unauthorized if the the passed user hasn't passed permission - for this instance. - - **Parameters:** - - permission - The permission's codename which should be checked. Must be a - string with a valid codename. - - user - The user for which the permission should be checked. - - roles - If passed, these roles will be assigned to the user temporarily - before the permissions are checked. - """ - if not self.has_permission(user, permission, roles): - raise Unauthorized("User %s doesn't have permission %s for object %s" % (user, permission, obj.slug)) - - def add_inheritance_block(self, permission): - """Adds an inheritance block for the passed permission. - - **Parameters:** - - permission - The permission for which an inheritance block should be added. - Either a permission object or the codename of a permission. - """ - return permissions.utils.add_inheritance_block(self, permission) - - def remove_inheritance_block(self, permission): - """Removes a inheritance block for the passed permission. - - **Parameters:** - - permission - The permission for which an inheritance block should be removed. - Either a permission object or the codename of a permission. - """ - return permissions.utils.remove_inheritance_block(self, permission) - - def is_inherited(self, codename): - """Returns True if the passed permission is inherited. - - **Parameters:** - - codename - The permission which should be checked. Must be the codename of - the permission. - """ - return permissions.utils.is_inherited(self, codename) - - def add_role(self, principal, role): - """Adds a local role for the principal. - - **Parameters:** - - principal - The principal (user or group) which gets the role. - - role - The role which is assigned. - """ - return permissions.utils.add_local_role(self, principal, role) - - def get_roles(self, principal): - """Returns *direct* local roles for passed principal (user or group). - """ - return permissions.utils.get_local_roles(self, principal) - - def remove_role(self, principal, role): - """Adds a local role for the principal to the object. - - **Parameters:** - - principal - The principal (user or group) from which the role is removed. - - role - The role which is removed. - """ - return permissions.utils.remove_local_role(self, principal, role) - - def remove_roles(self, principal): - """Removes all local roles for the passed principal from the object. - - **Parameters:** - - principal - The principal (user or group) from which all local roles are - removed. - """ - return permissions.utils.remove_local_roles(self, principal) \ No newline at end of file diff --git a/permissions/backend.py b/permissions/backend.py deleted file mode 100644 index a0c2d8ec6..000000000 --- a/permissions/backend.py +++ /dev/null @@ -1,45 +0,0 @@ -# permissions imports -import permissions.utils - -class ObjectPermissionsBackend(object): - """Django backend for object permissions. Needs Django 1.2. - - - Use it together with the default ModelBackend like so:: - - AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'permissions.backend.ObjectPermissionsBackend', - ) - - Then you can use it like: - - user.has_perm("view", your_object) - - """ - supports_object_permissions = True - supports_anonymous_user = True - - def authenticate(self, username, password): - return None - - def has_perm(self, user_obj, perm, obj=None): - """Checks whether the passed user has passed permission for passed - object (obj). - - This should be the primary method to check wether a user has a certain - permission. - - Parameters - ========== - - perm - The permission's codename which should be checked. - - user_obj - The user for which the permission should be checked. - - obj - The object for which the permission should be checked. - """ - return permissions.utils.has_permission(obj, user_obj, perm) \ No newline at end of file diff --git a/permissions/exceptions.py b/permissions/exceptions.py deleted file mode 100644 index 72c24a317..000000000 --- a/permissions/exceptions.py +++ /dev/null @@ -1,3 +0,0 @@ -class Unauthorized(Exception): - def __init__(self, str): - super(Unauthorized, self).__init__(str) \ No newline at end of file diff --git a/permissions/fixtures/initial.xml b/permissions/fixtures/initial.xml deleted file mode 100644 index 914abf8f1..000000000 --- a/permissions/fixtures/initial.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - View - view - - - Edit - edit - - - Delete - delete - - - Cut - cut - - - Copy - copy - - diff --git a/permissions/locale/de/LC_MESSAGES/django.mo b/permissions/locale/de/LC_MESSAGES/django.mo deleted file mode 100644 index 37b65347672e47df9ba769575d199a12b8943f51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmY+BF>ll`9K{V3C=o2MGI(M+xy{{4s7j%_v`LRh$=xZT?L#K>piGr-Ek^EI@>($mlX`1vV$`FVfm6T$MJhfo9EgML76{epI(-;jI$2Xfxs zYlPedAAmPO3hsc9KzBaq=f^z{!F^}~xxDuJ#r<>8`9Jjh2;Rc{8FYDHK$riu=Qq&Z z{|+K*>%9%$f!w-rfvpFY?Q;1p%jJ+0RSL;B0vlzhJMJpO3KdXq zF)!GO7Tm(lB5p-MNB+=f{+Nw@IvNMV(ZC=1K8!3EZ)+FZ`(DIh&ayd+Li%hTWXa&= zd=`5tHl)7IS3<)QWd@zioIe59w;nwGsB|vN&Z2S3dVHuZ3o@T&c2_ xRX`73)z-VJKap$RSmR2Tq#sxk{oCR>y6;;2f^GS^Sgoz=9mO9D+1d9f|1VGLqJaPa diff --git a/permissions/locale/de/LC_MESSAGES/django.po b/permissions/locale/de/LC_MESSAGES/django.po deleted file mode 100644 index 0a7e9e9cb..000000000 --- a/permissions/locale/de/LC_MESSAGES/django.po +++ /dev/null @@ -1,52 +0,0 @@ -# German translations for django-permissions -# Copyright (C) 2010 Kai Diefenbach -# This file is distributed under the same license as the PACKAGE package. -# Kai Diefenbach , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: 1.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-03-30 23:12+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: models.py:154 -msgid "Name" -msgstr "Name" - -#: models.py:155 -msgid "Codename" -msgstr "Codename" - -#: models.py:156 -msgid "Content Types" -msgstr "Inhaltstypen" - -#: models.py:175 models.py:280 -msgid "Role" -msgstr "Rolle" - -#: models.py:176 models.py:216 -msgid "Permission" -msgstr "Recht" - -#: models.py:178 models.py:218 models.py:282 -msgid "Content type" -msgstr "Inhaltstyp" - -#: models.py:179 models.py:219 models.py:283 -msgid "Content id" -msgstr "Inhalts-ID" - -#: models.py:278 -msgid "User" -msgstr "Benutzer" - -#: models.py:279 -msgid "Group" -msgstr "Gruppe" diff --git a/permissions/migrations/.gitignore b/permissions/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/permissions/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/permissions/migrations/0001_initial.py b/permissions/migrations/0001_initial.py deleted file mode 100644 index 38e36b067..000000000 --- a/permissions/migrations/0001_initial.py +++ /dev/null @@ -1,154 +0,0 @@ - -from south.db import db -from django.db import models -from permissions.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'Role' - db.create_table('permissions_role', ( - ('id', orm['permissions.Role:id']), - ('name', orm['permissions.Role:name']), - )) - db.send_create_signal('permissions', ['Role']) - - # Adding model 'ObjectPermissionInheritanceBlock' - db.create_table('permissions_objectpermissioninheritanceblock', ( - ('id', orm['permissions.ObjectPermissionInheritanceBlock:id']), - ('permission', orm['permissions.ObjectPermissionInheritanceBlock:permission']), - ('content_type', orm['permissions.ObjectPermissionInheritanceBlock:content_type']), - ('content_id', orm['permissions.ObjectPermissionInheritanceBlock:content_id']), - )) - db.send_create_signal('permissions', ['ObjectPermissionInheritanceBlock']) - - # Adding model 'ObjectPermission' - db.create_table('permissions_objectpermission', ( - ('id', orm['permissions.ObjectPermission:id']), - ('role', orm['permissions.ObjectPermission:role']), - ('permission', orm['permissions.ObjectPermission:permission']), - ('content_type', orm['permissions.ObjectPermission:content_type']), - ('content_id', orm['permissions.ObjectPermission:content_id']), - )) - db.send_create_signal('permissions', ['ObjectPermission']) - - # Adding model 'Permission' - db.create_table('permissions_permission', ( - ('id', orm['permissions.Permission:id']), - ('name', orm['permissions.Permission:name']), - ('codename', orm['permissions.Permission:codename']), - )) - db.send_create_signal('permissions', ['Permission']) - - # Adding model 'PrincipalRoleRelation' - db.create_table('permissions_principalrolerelation', ( - ('id', orm['permissions.PrincipalRoleRelation:id']), - ('user', orm['permissions.PrincipalRoleRelation:user']), - ('group', orm['permissions.PrincipalRoleRelation:group']), - ('role', orm['permissions.PrincipalRoleRelation:role']), - ('content_type', orm['permissions.PrincipalRoleRelation:content_type']), - ('content_id', orm['permissions.PrincipalRoleRelation:content_id']), - )) - db.send_create_signal('permissions', ['PrincipalRoleRelation']) - - # Adding ManyToManyField 'Permission.content_types' - db.create_table('permissions_permission_content_types', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('permission', models.ForeignKey(orm.Permission, null=False)), - ('contenttype', models.ForeignKey(orm['contenttypes.ContentType'], null=False)) - )) - - - - def backwards(self, orm): - - # Deleting model 'Role' - db.delete_table('permissions_role') - - # Deleting model 'ObjectPermissionInheritanceBlock' - db.delete_table('permissions_objectpermissioninheritanceblock') - - # Deleting model 'ObjectPermission' - db.delete_table('permissions_objectpermission') - - # Deleting model 'Permission' - db.delete_table('permissions_permission') - - # Deleting model 'PrincipalRoleRelation' - db.delete_table('permissions_principalrolerelation') - - # Dropping ManyToManyField 'Permission.content_types' - db.delete_table('permissions_permission_content_types') - - - - models = { - 'auth.group': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'unique_together': "(('content_type', 'codename'),)"}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'permissions.objectpermission': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']", 'null': 'True', 'blank': 'True'}) - }, - 'permissions.objectpermissioninheritanceblock': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'permissions.principalrolerelation': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) - }, - 'permissions.role': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - } - } - - complete_apps = ['permissions'] diff --git a/permissions/migrations/__init__.py b/permissions/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/permissions/models.py b/permissions/models.py deleted file mode 100644 index 478fbdf9d..000000000 --- a/permissions/models.py +++ /dev/null @@ -1,190 +0,0 @@ -# django imports -from django.db import models -from django.contrib.auth.models import User -from django.contrib.auth.models import Group -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -from django.utils.translation import ugettext_lazy as _ - -class Permission(models.Model): - """A permission which can be granted to users/groups and objects. - - **Attributes:** - - name - The unique name of the permission. This is displayed to users. - - codename - The unique codename of the permission. This is used internal to - identify a permission. - - content_types - The content types for which the permission is active. This can be - used to display only reasonable permissions for an object. - """ - name = models.CharField(_(u"Name"), max_length=100, unique=True) - codename = models.CharField(_(u"Codename"), max_length=100, unique=True) - content_types = models.ManyToManyField(ContentType, verbose_name=_(u"Content Types"), blank=True, null=True, related_name="content_types") - - def __unicode__(self): - return "%s (%s)" % (self.name, self.codename) - -class ObjectPermission(models.Model): - """Grants permission for specific user/group and object. - - **Attributes:** - - role - The role for which the permission is granted. - - permission - The permission which is granted. - - content - The object for which the permission is granted. - """ - role = models.ForeignKey("Role", verbose_name=_(u"Role"), blank=True, null=True) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type")) - content_id = models.PositiveIntegerField(verbose_name=_(u"Content id")) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - def __unicode__(self): - if self.role: - principal = self.role - else: - principal = self.user - - return "%s / %s / %s - %s" % (self.permission.name, principal, self.content_type, self.content_id) - - def get_principal(self): - """Returns the principal. - """ - return self.user or self.group - - def set_principal(self, principal): - """Sets the principal. - """ - if isinstance(principal, User): - self.user = principal - else: - self.group = principal - - principal = property(get_principal, set_principal) - -class ObjectPermissionInheritanceBlock(models.Model): - """Blocks the inheritance for specific permission and object. - - **Attributes:** - - permission - The permission for which inheritance is blocked. - - content - The object for which the inheritance is blocked. - """ - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type")) - content_id = models.PositiveIntegerField(verbose_name=_(u"Content id")) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - def __unicode__(self): - return "%s / %s - %s" % (self.permission, self.content_type, self.content_id) - -class Role(models.Model): - """A role gets permissions to do something. Principals (users and groups) - can only get permissions via roles. - - **Attributes:** - - name - The unique name of the role - """ - name = models.CharField(max_length=100, unique=True) - - class Meta: - ordering = ("name", ) - - def __unicode__(self): - return self.name - - def add_principal(self, principal, content=None): - """Addes the given principal (user or group) ot the Role. - """ - if isinstance(principal, User): - PrincipalRoleRelation.objects.create(user=principal, role=self) - else: - PrincipalRoleRelation.objects.create(group=principal, role=self) - - def get_groups(self, content=None): - """Returns all groups which has this role assigned. If content is given - it returns also the local roles. - """ - if content: - ctype = ContentType.objects.get_for_model(content) - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id__in = (None, content.pk), - content_type__in = (None, ctype)).exclude(group=None) - else: - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id=None, content_type=None).exclude(group=None) - - return [prr.group for prr in prrs] - - def get_users(self, content=None): - """Returns all users which has this role assigned. If content is given - it returns also the local roles. - """ - if content: - ctype = ContentType.objects.get_for_model(content) - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id__in = (None, content.pk), - content_type__in = (None, ctype)).exclude(user=None) - else: - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id=None, content_type=None).exclude(user=None) - - return [prr.user for prr in prrs] - -class PrincipalRoleRelation(models.Model): - """A role given to a principal (user or group). If a content object is - given this is a local role, i.e. the principal has this role only for this - content object. Otherwise it is a global role, i.e. the principal has - this role generally. - - user - A user instance. Either a user xor a group needs to be given. - - group - A group instance. Either a user xor a group needs to be given. - - role - The role which is given to the principal for content. - - content - The content object which gets the local role (optional). - """ - user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True) - group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True) - role = models.ForeignKey(Role, verbose_name=_(u"Role")) - - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), blank=True, null=True) - content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - def get_principal(self): - """Returns the principal. - """ - return self.user or self.group - - def set_principal(self, principal): - """Sets the principal. - """ - if isinstance(principal, User): - self.user = principal - else: - self.group = principal - - principal = property(get_principal, set_principal) diff --git a/permissions/templatetags/.gitignore b/permissions/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/permissions/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/permissions/templatetags/__init__.py b/permissions/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/permissions/templatetags/permissions_tags.py b/permissions/templatetags/permissions_tags.py deleted file mode 100644 index 94116381f..000000000 --- a/permissions/templatetags/permissions_tags.py +++ /dev/null @@ -1,48 +0,0 @@ -# django imports -from django import template -from django.core.exceptions import ImproperlyConfigured -from django.contrib.auth.models import User, AnonymousUser - -import permissions.utils -register = template.Library() - -class PermissionComparisonNode(template.Node): - """Implements a node to provide an if current user has passed permission - for current object. - """ - @classmethod - def handle_token(cls, parser, token): - bits = token.contents.split() - if len(bits) != 2: - raise template.TemplateSyntaxError( - "'%s' tag takes one argument" % bits[0]) - end_tag = 'endifhasperm' - nodelist_true = parser.parse(('else', end_tag)) - token = parser.next_token() - if token.contents == 'else': # there is an 'else' clause in the tag - nodelist_false = parser.parse((end_tag,)) - parser.delete_first_token() - else: - nodelist_false = "" - - return cls(bits[1], nodelist_true, nodelist_false) - - def __init__(self, permission, nodelist_true, nodelist_false): - self.permission = permission - self.nodelist_true = nodelist_true - self.nodelist_false = nodelist_false - - def render(self, context): - obj = context.get("obj") - request = context.get("request") - if permissions.utils.has_permission(self.permission, request.user, obj): - return self.nodelist_true.render(context) - else: - return self.nodelist_false - -@register.tag -def ifhasperm(parser, token): - """This function provides functionality for the 'ifhasperm' template tag. - """ - return PermissionComparisonNode.handle_token(parser, token) - diff --git a/permissions/tests.py b/permissions/tests.py deleted file mode 100644 index 91bbf51bd..000000000 --- a/permissions/tests.py +++ /dev/null @@ -1,783 +0,0 @@ -# django imports -from django.contrib.flatpages.models import FlatPage -from django.contrib.auth.models import Group -from django.contrib.auth.models import User -from django.conf import settings -from django.core.urlresolvers import reverse -from django.test import TestCase -from django.test.client import Client - -# permissions imports -from permissions.models import Permission -from permissions.models import ObjectPermission -from permissions.models import ObjectPermissionInheritanceBlock -from permissions.models import Role - -import permissions.utils - -class BackendTestCase(TestCase): - """ - """ - def setUp(self): - """ - """ - settings.AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'permissions.backend.ObjectPermissionsBackend', - ) - - self.role_1 = permissions.utils.register_role("Role 1") - self.user = User.objects.create(username="john") - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - self.view = permissions.utils.register_permission("View", "view") - - # Add user to role - self.role_1.add_principal(self.user) - - def test_has_perm(self): - """Tests has perm of the backend. - """ - result = self.user.has_perm(self.view, self.page_1) - self.assertEqual(result, False) - - # assign view permission to role 1 - permissions.utils.grant_permission(self.page_1, self.role_1, self.view) - - result = self.user.has_perm("view", self.page_1) - self.assertEqual(result, True) - -class RoleTestCase(TestCase): - """ - """ - def setUp(self): - """ - """ - self.role_1 = permissions.utils.register_role("Role 1") - self.role_2 = permissions.utils.register_role("Role 2") - - self.user = User.objects.create(username="john") - self.group = Group.objects.create(name="brights") - - self.user.groups.add(self.group) - - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2") - - def test_getter(self): - """ - """ - result = permissions.utils.get_group(self.group.id) - self.assertEqual(result, self.group) - - result = permissions.utils.get_group(42) - self.assertEqual(result, None) - - result = permissions.utils.get_role(self.role_1.id) - self.assertEqual(result, self.role_1) - - result = permissions.utils.get_role(42) - self.assertEqual(result, None) - - result = permissions.utils.get_user(self.user.id) - self.assertEqual(result, self.user) - - result = permissions.utils.get_user(42) - self.assertEqual(result, None) - - def test_global_roles_user(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Add role 1 again - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_1]) - - # Add role 2 - result = permissions.utils.add_role(self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_role(self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_role(self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, []) - - def test_global_roles_group(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Add role 1 again - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_1]) - - # Add role 2 - result = permissions.utils.add_role(self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_role(self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_role(self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, []) - - def test_remove_roles_user(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Add role 2 - result = permissions.utils.add_role(self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove roles - result = permissions.utils.remove_roles(self.user) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, []) - - # Remove roles - result = permissions.utils.remove_roles(self.user) - self.assertEqual(result, False) - - def test_remove_roles_group(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Add role 2 - result = permissions.utils.add_role(self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove roles - result = permissions.utils.remove_roles(self.group) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, []) - - # Remove roles - result = permissions.utils.remove_roles(self.group) - self.assertEqual(result, False) - - def test_local_role_user(self): - """ - """ - # Add local role to page 1 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # Again - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_1]) - - # Add local role 2 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, []) - - def test_local_role_group(self): - """ - """ - # Add local role to page 1 - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - # Again - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, [self.role_1]) - - # Add local role 2 - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, []) - - def test_remove_local_roles_user(self): - """ - """ - # Add local role to page 1 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # Add local role 2 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove all local roles - result = permissions.utils.remove_local_roles(self.page_1, self.user) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, []) - - # Remove all local roles again - result = permissions.utils.remove_local_roles(self.page_1, self.user) - self.assertEqual(result, False) - - def test_get_groups_1(self): - """Tests global roles for groups. - """ - result = self.role_1.get_groups() - self.assertEqual(len(result), 0) - - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - - # Add another group - self.group_2 = Group.objects.create(name="atheists") - result = permissions.utils.add_role(self.group_2, self.role_1) - - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - # Add the role to an user - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - def test_get_groups_2(self): - """Tests local roles for groups. - """ - result = self.role_1.get_groups(self.page_1) - self.assertEqual(len(result), 0) - - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - - # Add another local group - self.group_2 = Group.objects.create(name="atheists") - result = permissions.utils.add_local_role(self.page_1, self.group_2, self.role_1) - - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - - # A the global role to group - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Nontheless there are just two groups returned (and no duplicate) - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - # Andere there should one global role - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - - # Add the role to an user - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - def test_get_users_1(self): - """Tests global roles for users. - """ - result = self.role_1.get_users() - self.assertEqual(len(result), 0) - - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - - # Add another role to an user - self.user_2 = User.objects.create(username="jane") - result = permissions.utils.add_role(self.user_2, self.role_1) - - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - - # Add the role to an user - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - - def test_get_users_2(self): - """Tests local roles for users. - """ - result = self.role_1.get_users(self.page_1) - self.assertEqual(len(result), 0) - - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - - # Add another local role to an user - self.user_2 = User.objects.create(username="jane") - result = permissions.utils.add_local_role(self.page_1, self.user_2, self.role_1) - - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - - # A the global role to user - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Nontheless there are just two users returned (and no duplicate) - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - - # Andere there should one user for the global role - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - - # Add the role to an group - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - -class PermissionTestCase(TestCase): - """ - """ - def setUp(self): - """ - """ - self.role_1 = permissions.utils.register_role("Role 1") - self.role_2 = permissions.utils.register_role("Role 2") - - self.user = User.objects.create(username="john") - permissions.utils.add_role(self.user, self.role_1) - self.user.save() - - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2") - - self.permission = permissions.utils.register_permission("View", "view") - - def test_add_permissions(self): - """ - """ - # Add per object - result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) - self.assertEqual(result, True) - - # Add per codename - result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - # Add ermission which does not exist - result = permissions.utils.grant_permission(self.page_1, self.role_1, "hurz") - self.assertEqual(result, False) - - def test_remove_permission(self): - """ - """ - # Add - result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - # Remove - result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - # Remove again - result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, False) - - def test_has_permission_role(self): - """ - """ - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - def test_has_permission_owner(self): - """ - """ - creator = User.objects.create(username="jane") - - result = permissions.utils.has_permission(self.page_1, creator, "view") - self.assertEqual(result, False) - - owner = permissions.utils.register_role("Owner") - permissions.utils.grant_permission(self.page_1, owner, "view") - - result = permissions.utils.has_permission(self.page_1, creator, "view", [owner]) - self.assertEqual(result, True) - - def test_local_role(self): - """ - """ - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - permissions.utils.grant_permission(self.page_1, self.role_2, self.permission) - permissions.utils.add_local_role(self.page_1, self.user, self.role_2) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - def test_ineritance(self): - """ - """ - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - # per permission - permissions.utils.add_inheritance_block(self.page_1, self.permission) - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - permissions.utils.remove_inheritance_block(self.page_1, self.permission) - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - # per codename - permissions.utils.add_inheritance_block(self.page_1, "view") - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - permissions.utils.remove_inheritance_block(self.page_1, "view") - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - def test_unicode(self): - """ - """ - # Permission - self.assertEqual(self.permission.__unicode__(), "View (view)") - - # ObjectPermission - permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) - opr = ObjectPermission.objects.get(permission=self.permission, role=self.role_1) - self.assertEqual(opr.__unicode__(), "View / Role 1 / flat page - 1") - - # ObjectPermissionInheritanceBlock - permissions.utils.add_inheritance_block(self.page_1, self.permission) - opb = ObjectPermissionInheritanceBlock.objects.get(permission=self.permission) - - self.assertEqual(opb.__unicode__(), "View (view) / flat page - 1") - - def test_reset(self): - """ - """ - result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - permissions.utils.add_inheritance_block(self.page_1, "view") - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - permissions.utils.reset(self.page_1) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - permissions.utils.reset(self.page_1) - -class RegistrationTestCase(TestCase): - """Tests the registration of different components. - """ - def test_group(self): - """Tests registering/unregistering of a group. - """ - # Register a group - result = permissions.utils.register_group("Brights") - self.failUnless(isinstance(result, Group)) - - # It's there - group = Group.objects.get(name="Brights") - self.assertEqual(group.name, "Brights") - - # Trying to register another group with same name - result = permissions.utils.register_group("Brights") - self.assertEqual(result, False) - - group = Group.objects.get(name="Brights") - self.assertEqual(group.name, "Brights") - - # Unregister the group - result = permissions.utils.unregister_group("Brights") - self.assertEqual(result, True) - - # It's not there anymore - self.assertRaises(Group.DoesNotExist, Group.objects.get, name="Brights") - - # Trying to unregister the group again - result = permissions.utils.unregister_group("Brights") - self.assertEqual(result, False) - - def test_role(self): - """Tests registering/unregistering of a role. - """ - # Register a role - result = permissions.utils.register_role("Editor") - self.failUnless(isinstance(result, Role)) - - # It's there - role = Role.objects.get(name="Editor") - self.assertEqual(role.name, "Editor") - - # Trying to register another role with same name - result = permissions.utils.register_role("Editor") - self.assertEqual(result, False) - - role = Role.objects.get(name="Editor") - self.assertEqual(role.name, "Editor") - - # Unregister the role - result = permissions.utils.unregister_role("Editor") - self.assertEqual(result, True) - - # It's not there anymore - self.assertRaises(Role.DoesNotExist, Role.objects.get, name="Editor") - - # Trying to unregister the role again - result = permissions.utils.unregister_role("Editor") - self.assertEqual(result, False) - - def test_permission(self): - """Tests registering/unregistering of a permission. - """ - # Register a permission - result = permissions.utils.register_permission("Change", "change") - self.failUnless(isinstance(result, Permission)) - - # Is it there? - p = Permission.objects.get(codename="change") - self.assertEqual(p.name, "Change") - - # Register a permission with the same codename - result = permissions.utils.register_permission("Change2", "change") - self.assertEqual(result, False) - - # Is it there? - p = Permission.objects.get(codename="change") - self.assertEqual(p.name, "Change") - - # Register a permission with the same name - result = permissions.utils.register_permission("Change", "change2") - self.assertEqual(result, False) - - # Is it there? - p = Permission.objects.get(codename="change") - self.assertEqual(p.name, "Change") - - # Unregister the permission - result = permissions.utils.unregister_permission("change") - self.assertEqual(result, True) - - # Is it not there anymore? - self.assertRaises(Permission.DoesNotExist, Permission.objects.get, codename="change") - - # Unregister the permission again - result = permissions.utils.unregister_permission("change") - self.assertEqual(result, False) - -# django imports -from django.core.handlers.wsgi import WSGIRequest -from django.contrib.auth.models import User -from django.contrib.sessions.backends.file import SessionStore -from django.test.client import Client - -# Taken from "http://www.djangosnippets.org/snippets/963/" -class RequestFactory(Client): - """ - Class that lets you create mock Request objects for use in testing. - - Usage: - - rf = RequestFactory() - get_request = rf.get('/hello/') - post_request = rf.post('/submit/', {'foo': 'bar'}) - - This class re-uses the django.test.client.Client interface, docs here: - http://www.djangoproject.com/documentation/testing/#the-test-client - - Once you have a request object you can pass it to any view function, - just as if that view had been hooked up using a URLconf. - - """ - def request(self, **request): - """ - Similar to parent class, but returns the request object as soon as it - has created it. - """ - environ = { - 'HTTP_COOKIE': self.cookies, - 'PATH_INFO': '/', - 'QUERY_STRING': '', - 'REQUEST_METHOD': 'GET', - 'SCRIPT_NAME': '', - 'SERVER_NAME': 'testserver', - 'SERVER_PORT': 80, - 'SERVER_PROTOCOL': 'HTTP/1.1', - } - environ.update(self.defaults) - environ.update(request) - return WSGIRequest(environ) - -def create_request(): - """ - """ - rf = RequestFactory() - request = rf.get('/') - request.session = SessionStore() - - user = User() - user.is_superuser = True - user.save() - request.user = user - - return request \ No newline at end of file diff --git a/permissions/utils.py b/permissions/utils.py deleted file mode 100644 index 06183e946..000000000 --- a/permissions/utils.py +++ /dev/null @@ -1,665 +0,0 @@ -# django imports -from django.db import IntegrityError -from django.db.models import Q -from django.db import connection -from django.contrib.auth.models import User -from django.contrib.auth.models import Group -from django.contrib.contenttypes.models import ContentType -from django.core.cache import cache -from django.core.exceptions import ObjectDoesNotExist - -# permissions imports -from permissions.exceptions import Unauthorized -from permissions.models import ObjectPermission -from permissions.models import ObjectPermissionInheritanceBlock -from permissions.models import Permission -from permissions.models import PrincipalRoleRelation -from permissions.models import Role - - -# Roles ###################################################################### - -def add_role(principal, role): - """Adds a global role to a principal. - - **Parameters:** - - principal - The principal (user or group) which gets the role added. - - role - The role which is assigned. - """ - if isinstance(principal, User): - try: - ppr = PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=None, content_type=None) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(user=principal, role=role) - return True - else: - try: - ppr = PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=None, content_type=None) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(group=principal, role=role) - return True - - return False - -def add_local_role(obj, principal, role): - """Adds a local role to a principal. - - **Parameters:** - - obj - The object for which the principal gets the role. - - principal - The principal (user or group) which gets the role. - - role - The role which is assigned. - """ - ctype = ContentType.objects.get_for_model(obj) - if isinstance(principal, User): - try: - ppr = PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=obj.pk, content_type=ctype) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(user=principal, role=role, content=obj) - return True - else: - try: - ppr = PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=obj.pk, content_type=ctype) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(group=principal, role=role, content=obj) - return True - - return False - -def remove_role(principal, role): - """Removes role from passed principal. - - **Parameters:** - - principal - The principal (user or group) from which the role is removed. - - role - The role which is removed. - """ - try: - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.get( - user=principal, role=role, content_id=None, content_type=None) - else: - ppr = PrincipalRoleRelation.objects.get( - group=principal, role=role, content_id=None, content_type=None) - - except PrincipalRoleRelation.DoesNotExist: - return False - else: - ppr.delete() - - return True - -def remove_local_role(obj, principal, role): - """Removes role from passed object and principle. - - **Parameters:** - - obj - The object from which the role is removed. - - principal - The principal (user or group) from which the role is removed. - - role - The role which is removed. - """ - try: - ctype = ContentType.objects.get_for_model(obj) - - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.get( - user=principal, role=role, content_id=obj.pk, content_type=ctype) - else: - ppr = PrincipalRoleRelation.objects.get( - group=principal, role=role, content_id=obj.pk, content_type=ctype) - - except PrincipalRoleRelation.DoesNotExist: - return False - else: - ppr.delete() - - return True - -def remove_roles(principal): - """Removes all roles passed principal (user or group). - - **Parameters:** - - principal - The principal (user or group) from which all roles are removed. - """ - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.filter( - user=principal, content_id=None, content_type=None) - else: - ppr = PrincipalRoleRelation.objects.filter( - group=principal, content_id=None, content_type=None) - - if ppr: - ppr.delete() - return True - else: - return False - -def remove_local_roles(obj, principal): - """Removes all local roles from passed object and principal (user or - group). - - **Parameters:** - - obj - The object from which the roles are removed. - - principal - The principal (user or group) from which the roles are removed. - """ - ctype = ContentType.objects.get_for_model(obj) - - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.filter( - user=principal, content_id=obj.pk, content_type=ctype) - else: - ppr = PrincipalRoleRelation.objects.filter( - group=principal, content_id=obj.pk, content_type=ctype) - - if ppr: - ppr.delete() - return True - else: - return False - -def get_roles(user, obj=None): - """Returns *all* roles of the passed user. - - This takes direct roles and roles via the user's groups into account. - - If an object is passed local roles will also added. Then all local roles - from all ancestors and all user's groups are also taken into account. - - This is the method to use if one want to know whether the passed user - has a role in general (for the passed object). - - **Parameters:** - - user - The user for which the roles are returned. - - obj - The object for which local roles will returned. - - """ - roles = [] - groups = user.groups.all() - groups_ids_str = ", ".join([str(g.id) for g in groups]) - - # Gobal roles for user and the user's groups - cursor = connection.cursor() - cursor.execute("""SELECT role_id - FROM permissions_principalrolerelation - WHERE (user_id=%s OR group_id IN (%s)) - AND content_id is Null""" % (user.id, groups_ids_str)) - - for row in cursor.fetchall(): - roles.append(row[0]) - - # Local roles for user and the user's groups and all ancestors of the - # passed object. - while obj: - ctype = ContentType.objects.get_for_model(obj) - cursor.execute("""SELECT role_id - FROM permissions_principalrolerelation - WHERE (user_id='%s' OR group_id IN (%s)) - AND content_id='%s' - AND content_type_id='%s'""" % (user.id, groups_ids_str, obj.pk, ctype.id)) - - for row in cursor.fetchall(): - roles.append(row[0]) - - try: - obj = obj.get_parent_for_permissions() - except AttributeError: - obj = None - - return roles - -def get_global_roles(principal): - """Returns *direct* global roles of passed principal (user or group). - """ - if isinstance(principal, User): - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - user=principal, content_id=None, content_type=None)] - else: - if isinstance(principal, Group): - principal = (principal,) - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - group__in=principal, content_id=None, content_type=None)] - -def get_local_roles(obj, principal): - """Returns *direct* local roles for passed principal and content object. - """ - ctype = ContentType.objects.get_for_model(obj) - - if isinstance(principal, User): - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - user=principal, content_id=obj.pk, content_type=ctype)] - else: - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - group=principal, content_id=obj.pk, content_type=ctype)] - -# Permissions ################################################################ - -def check_permission(obj, user, codename, roles=None): - """Checks whether passed user has passed permission for passed obj. - - **Parameters:** - - obj - The object for which the permission should be checked. - - codename - The permission's codename which should be checked. - - user - The user for which the permission should be checked. - - roles - If given these roles will be assigned to the user temporarily before - the permissions are checked. - """ - if not has_permission(obj, user, codename): - raise Unauthorized("User '%s' doesn't have permission '%s' for object '%s' (%s)" - % (user, codename, obj.slug, obj.__class__.__name__)) - -def grant_permission(obj, role, permission): - """Grants passed permission to passed role. Returns True if the permission - was able to be added, otherwise False. - - **Parameters:** - - obj - The content object for which the permission should be granted. - - role - The role for which the permission should be granted. - - permission - The permission which should be granted. Either a permission - object or the codename of a permission. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - try: - ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.pk, permission=permission) - except ObjectPermission.DoesNotExist: - ObjectPermission.objects.create(role=role, content=obj, permission=permission) - - return True - -def remove_permission(obj, role, permission): - """Removes passed permission from passed role and object. Returns True if - the permission has been removed. - - **Parameters:** - - obj - The content object for which a permission should be removed. - - role - The role for which a permission should be removed. - - permission - The permission which should be removed. Either a permission object - or the codename of a permission. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - - try: - op = ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.pk, permission = permission) - except ObjectPermission.DoesNotExist: - return False - - op.delete() - return True - -def has_permission(obj, user, codename, roles=None): - """Checks whether the passed user has passed permission for passed object. - - **Parameters:** - - obj - The object for which the permission should be checked. - - codename - The permission's codename which should be checked. - - request - The current request. - - roles - If given these roles will be assigned to the user temporarily before - the permissions are checked. - """ - cache_key = "%s-%s-%s" % (obj.content_type, obj.pk, codename) - result = _get_cached_permission(user, cache_key) - if result is not None: - return result - - if roles is None: - roles = [] - - if user.is_superuser: - return True - - if not user.is_anonymous(): - roles.extend(get_roles(user, obj)) - - ct = ContentType.objects.get_for_model(obj) - - result = False - while obj is not None: - p = ObjectPermission.objects.filter( - content_type=ct, content_id=obj.pk, role__in=roles, permission__codename = codename).values("id") - - if len(p) > 0: - result = True - break - - if is_inherited(obj, codename) == False: - result = False - break - - try: - obj = obj.get_parent_for_permissions() - ct = ContentType.objects.get_for_model(obj) - except AttributeError: - result = False - break - - _cache_permission(user, cache_key, result) - return result - -# Inheritance ################################################################ - -def add_inheritance_block(obj, permission): - """Adds an inheritance for the passed permission on the passed obj. - - **Parameters:** - - permission - The permission for which an inheritance block should be added. - Either a permission object or the codename of a permission. - obj - The content object for which an inheritance block should be added. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - try: - ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.pk, permission=permission) - except ObjectPermissionInheritanceBlock.DoesNotExist: - try: - result = ObjectPermissionInheritanceBlock.objects.create(content=obj, permission=permission) - except IntegrityError: - return False - return True - -def remove_inheritance_block(obj, permission): - """Removes a inheritance block for the passed permission from the passed - object. - - **Parameters:** - - obj - The content object for which an inheritance block should be added. - - permission - The permission for which an inheritance block should be removed. - Either a permission object or the codename of a permission. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - try: - opi = ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.pk, permission=permission) - except ObjectPermissionInheritanceBlock.DoesNotExist: - return False - - opi.delete() - return True - -def is_inherited(obj, codename): - """Returns True if the passed permission is inherited for passed object. - - **Parameters:** - - obj - The content object for which the permission should be checked. - - codename - The permission which should be checked. Must be the codename of - the permission. - """ - ct = ContentType.objects.get_for_model(obj) - try: - ObjectPermissionInheritanceBlock.objects.get( - content_type=ct, content_id=obj.pk, permission__codename = codename) - except ObjectDoesNotExist: - return True - else: - return False - -def get_group(id): - """Returns the group with passed id or None. - """ - try: - return Group.objects.get(pk=id) - except Group.DoesNotExist: - return None - -def get_role(id): - """Returns the role with passed id or None. - """ - try: - return Role.objects.get(pk=id) - except Role.DoesNotExist: - return None - -def get_user(id): - """Returns the user with passed id or None. - """ - try: - return User.objects.get(pk=id) - except User.DoesNotExist: - return None - -def has_group(user, group): - """Returns True if passed user has passed group. - """ - if isinstance(group, str): - group = Group.objects.get(name=group) - - return group in user.groups.all() - -def reset(obj): - """Resets all permissions and inheritance blocks of passed object. - """ - ctype = ContentType.objects.get_for_model(obj) - ObjectPermissionInheritanceBlock.objects.filter(content_id=obj.pk, content_type=ctype).delete() - ObjectPermission.objects.filter(content_id=obj.pk, content_type=ctype).delete() - -# Registering ################################################################ - -def register_permission(name, codename, ctypes=[]): - """Registers a permission to the framework. Returns the permission if the - registration was successfully, otherwise False. - - **Parameters:** - - name - The unique name of the permission. This is displayed to the - customer. - codename - The unique codename of the permission. This is used internally to - identify the permission. - content_types - The content type for which the permission is active. This can be - used to display only reasonable permissions for an object. This - must be a Django ContentType - """ - try: - p = Permission.objects.create(name=name, codename=codename) - - ctypes = [ContentType.objects.get_for_model(ctype) for ctype in ctypes] - if ctypes: - p.content_types = ctypes - p.save() - except IntegrityError: - return False - return p - -def unregister_permission(codename): - """Unregisters a permission from the framework - - **Parameters:** - - codename - The unique codename of the permission. - """ - try: - permission = Permission.objects.get(codename=codename) - except Permission.DoesNotExist: - return False - permission.delete() - return True - -def register_role(name): - """Registers a role with passed name to the framework. Returns the new - role if the registration was successfully, otherwise False. - - **Parameters:** - - name - The unique role name. - """ - try: - role = Role.objects.create(name=name) - except IntegrityError: - return False - return role - -def unregister_role(name): - """Unregisters the role with passed name. - - **Parameters:** - - name - The unique role name. - """ - try: - role = Role.objects.get(name=name) - except Role.DoesNotExist: - return False - - role.delete() - return True - -def register_group(name): - """Registers a group with passed name to the framework. Returns the new - group if the registration was successfully, otherwise False. - - Actually this creates just a default Django Group. - - **Parameters:** - - name - The unique group name. - """ - try: - group = Group.objects.create(name=name) - except IntegrityError: - return False - return group - -def unregister_group(name): - """Unregisters the group with passed name. Returns True if the - unregistration was succesfull otherwise False. - - Actually this deletes just a default Django Group. - - **Parameters:** - - name - The unique role name. - """ - try: - group = Group.objects.get(name=name) - except Group.DoesNotExist: - return False - - group.delete() - return True - -def _cache_permission(user, cache_key, data): - """Stores the passed data on the passed user object. - - **Parameters:** - - user - The user on which the data is stored. - - cache_key - The key under which the data is stored. - - data - The data which is stored. - """ - if not getattr(user, "permissions", None): - user.permissions = {} - user.permissions[cache_key] = data - -def _get_cached_permission(user, cache_key): - """Returns the stored data from passed user object for passed cache_key. - - **Parameters:** - - user - The user from which the data is retrieved. - - cache_key - The key under which the data is stored. - - """ - permissions = getattr(user, "permissions", None) - if permissions: - return user.permissions.get(cache_key, None) From 3701f2ac7d6ecb0f77daba7b68169b865d6bd641 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sat, 28 Sep 2013 21:10:28 +0000 Subject: [PATCH 046/173] Use Role rather than Email when retrieving roles in wginfo - Role has a link to both the email and the person (but didn't originally with the new schema) so is better than the going through the email only, do a couple of extra cleanups too while at it - Legacy-Id: 6301 --- ietf/templates/wginfo/1wg-summary.txt | 2 +- ietf/templates/wginfo/active_wgs.html | 47 ++++++++--------- ietf/templates/wginfo/conclude.html | 13 ++--- ietf/templates/wginfo/group_base.html | 15 ++---- ietf/templates/wginfo/group_charter.html | 18 +++---- ietf/templates/wginfo/group_entry.txt | 2 +- .../wginfo/group_entry_with_charter.txt | 10 ++-- ietf/wginfo/views.py | 50 ++++++++----------- 8 files changed, 69 insertions(+), 88 deletions(-) diff --git a/ietf/templates/wginfo/1wg-summary.txt b/ietf/templates/wginfo/1wg-summary.txt index 8073c03c7..f6ae51202 100644 --- a/ietf/templates/wginfo/1wg-summary.txt +++ b/ietf/templates/wginfo/1wg-summary.txt @@ -4,7 +4,7 @@ {% for area in areas %}{{ area.name }} ({{ area.acronym }}) {{ area.name|dashify }}------{% for ad in area.ads %} - {{ ad.person }} <{{ ad.address }}>{% endfor %} + {{ ad.person.plain_name }} <{{ ad.email.address }}>{% endfor %} {% for group in area.groups %}{{ group.name }} ({{ group.acronym }}) {% include "wginfo/group_entry.txt" %} diff --git a/ietf/templates/wginfo/active_wgs.html b/ietf/templates/wginfo/active_wgs.html index a74f48ba9..4d88435fa 100644 --- a/ietf/templates/wginfo/active_wgs.html +++ b/ietf/templates/wginfo/active_wgs.html @@ -53,47 +53,44 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% for area in areas %}

      {{ area.name }}

      - {% for ad in area.ads %} - {% if forloop.first %} -

      Area Director{{ forloop.revcounter|pluralize }}:

      + {% if area.ads %} +

      Area Director{{ area.ads|pluralize }}:

      - {% endif %} - - {% if forloop.last %} + {% for ad in area.ads %} + + + + + {% endfor %}
        {{ ad.person.plain_name }} <{{ ad.address }}>{% if ad.incoming %} (Incoming AD){% endif %}
       {{ ad.person.plain_name }} <{{ ad.email.address }}>{% if ad.name == "pre-ad" %} (Incoming AD){% endif %}
      {% endif %} - {% endfor %} - {% for url in area.urls %} - {% if forloop.first %} -

      Area Specific Web Page{{ forloop.revcounter|pluralize}}:

      + {% if area.urls %} +

      Area Specific Web Page{{ area.urls|pluralize}}:

      - {% endif %} + {% for url in area.urls %} {{ url.name }}{% if not forloop.last %}
      {% endif %} - {% if forloop.last %} + {% endfor %}

      {% endif %} - {% endfor %} - {% for wg in area.wgs %} - {% if forloop.first %} -

      Active Working Groups:

      + {% if area.groups %} +

      Active Working Group{{ area.groups|pluralize}}:

      - {% endif %} + {% for group in area.groups %} - - - - + + + + - {% if forloop.last %} + {% endfor %}
      {{ wg.acronym }}{% for ad in area.ads %}{% ifequal ad.person_id wg.ad_id %}{% endifequal %}{% endfor %}{{ wg.name }}{% for chair in wg.chairs %}{{ chair.person.plain_name }}{% if not forloop.last %}, {% endif %}{% endfor %}{{ group.acronym }}{% for ad in area.ads %}{% if ad.person_id == group.ad_id %}{% endif %}{% endfor %}{{ group.name }}{% for chair in group.chairs %}{{ chair.person.plain_name }}{% if not forloop.last %}, {% endif %}{% endfor %}
      - {% endif %} - {% empty %} + {% else %}

      No Active Working Groups

      - {% endfor %}{# wg #} + {% endif %} {% endfor %}{# area #} diff --git a/ietf/templates/wginfo/conclude.html b/ietf/templates/wginfo/conclude.html index 76391dcef..76f0ad03c 100644 --- a/ietf/templates/wginfo/conclude.html +++ b/ietf/templates/wginfo/conclude.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Request closing of WG {{ wg.acronym }}{% endblock %} +{% block title %}Request closing of {{ wg.acronym }} {{ wg.type.name }}{% endblock %} {% block morecss %} #id_instructions { @@ -14,22 +14,23 @@ form.conclude .actions { {% endblock %} {% block content %} -

      Request closing of {{ wg.acronym }}

      +

      Request closing of {{ wg.acronym }} {{ wg.type.name }}

      Please provide instructions regarding the disposition of each active Internet-Draft (such as to withdraw the draft, move it to - another WG, convert it to an individual submission, and so on), - wording for the closure announcement, and the status of the WG + another group, convert it to an individual submission, and so on), + wording for the closure announcement, and the status of the group mailing list (will it remain open or should it be closed).

      +
      {{ form.as_table }}
      - Back - + Cancel +
      diff --git a/ietf/templates/wginfo/group_base.html b/ietf/templates/wginfo/group_base.html index 6e1f7907e..a229f60cc 100644 --- a/ietf/templates/wginfo/group_base.html +++ b/ietf/templates/wginfo/group_base.html @@ -51,14 +51,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .ietf-concluded-warning { background:red;color:white;padding:2px 2px;} .ietf-proposed-bg { } .ietf-proposed-warning { background:green;color:white;padding:2px 2px;} -.ietf-box th { - font-weight: bold; - padding-top: 1em; - text-align: left; -} -.ietf-box tr:first-child th { - padding-top: 0; -} +.ietf-box th { font-weight: bold; padding-top: 1em; text-align: left; } +.ietf-box tr:first-child th { padding-top: 0; } {% endblock morecss %} {% block content %} @@ -73,7 +67,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      Documents | Charter | - History | {% if group.list_archive|startswith:"http:" or group.list_archive|startswith:"https:" or group.list_archive|startswith:"ftp:" %} List Archive » | @@ -83,9 +76,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% if menu_actions %}
      - {% for name, url in menu_actions %} + {% for name, url in menu_actions %} {{ name }} - {% endfor %} + {% endfor %}
      {% endif %}
      diff --git a/ietf/templates/wginfo/group_charter.html b/ietf/templates/wginfo/group_charter.html index 8148bc035..1f39ac850 100644 --- a/ietf/templates/wginfo/group_charter.html +++ b/ietf/templates/wginfo/group_charter.html @@ -59,7 +59,7 @@ is occasionally incorrect. Chair{{ group.chairs|pluralize }}: {% for chair in group.chairs %} - {{ chair.person.plain_name }} <{{ chair.address }}>
      + {{ chair.person.plain_name }} <{{ chair.email.address }}>
      {% endfor %} @@ -77,7 +77,7 @@ is occasionally incorrect. Tech Advisor{{ group.techadvisors|pluralize }}: {% for techadvisor in group.techadvisors %} - {{ techadvisor.person.plain_name }} <{{ techadvisor.address }}>
      + {{ techadvisor.person.plain_name }} <{{ techadvisor.email.address }}>
      {% endfor %} @@ -88,7 +88,7 @@ is occasionally incorrect. Editor{{ group.editors|pluralize }}: {% for editor in group.editors %} - {{ editor.person.plain_name }} <{{ editor.address }}>
      + {{ editor.person.plain_name }} <{{ editor.email.address }}>
      {% endfor %} @@ -99,7 +99,7 @@ is occasionally incorrect. Secretar{{ group.secretaries|pluralize:"y,ies" }}: {% for secretary in group.secretaries %} - {{ secretary.person.plain_name }} <{{ secretary.address }}>
      + {{ secretary.person.plain_name }} <{{ secretary.email.address }}>
      {% endfor %} @@ -110,7 +110,7 @@ is occasionally incorrect. Delegate{{ group.delegates|pluralize }}: {% for delegate in group.delegates %} - {{ delegate.person.plain_name }} <{{ delegate.address }}>
      + {{ delegate.person.plain_name }} <{{ delegate.email.address }}>
      {% endfor %} @@ -142,10 +142,10 @@ is occasionally incorrect. {% with group.groupurl_set.all as urls %} {% if urls %} -

      In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at: -{% for url in urls %} -{{ url.name }}{% if not forloop.last %}, {% endif %} -{% endfor %} +

      In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at: + {% for url in urls %} + {{ url.name }}{% if not forloop.last %}, {% endif %} + {% endfor %}

      {% endif %} {% endwith %} diff --git a/ietf/templates/wginfo/group_entry.txt b/ietf/templates/wginfo/group_entry.txt index c58fca075..cd8d4b60c 100644 --- a/ietf/templates/wginfo/group_entry.txt +++ b/ietf/templates/wginfo/group_entry.txt @@ -1,4 +1,4 @@ -{% for chair in group.chairs %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person.plain_name }} <{{ chair.address }}> +{% for chair in group.chairs %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person.plain_name }} <{{ chair.email.address }}> {% endfor %} WG Mail: {{ group.list_email }} To Join: {{ group.list_subscribe }} Archive: {{ group.list_archive }} diff --git a/ietf/templates/wginfo/group_entry_with_charter.txt b/ietf/templates/wginfo/group_entry_with_charter.txt index fb904032a..abde2531a 100644 --- a/ietf/templates/wginfo/group_entry_with_charter.txt +++ b/ietf/templates/wginfo/group_entry_with_charter.txt @@ -7,22 +7,22 @@ Current Status: {{ group.state.name }} Chair{{ group.chairs|pluralize }}: -{% for chair in group.chairs %} {{ chair.person.name }} <{{chair.address}}> +{% for chair in group.chairs %} {{ chair.person.name }} <{{chair.email.address}}> {% endfor %} {{ group.area.name}} Directors: -{% for ad in group.area.ads %} {{ ad.person.plain_name }} <{{ ad }}> +{% for ad in group.area.ads %} {{ ad.person.plain_name }} <{{ ad.email.address }}> {% endfor %} {% if group.areadirector %} {{ group.area.name }} Advisor: {{ group.areadirector.person.plain_name }} <{{ group.areadirector.address }}> {% endif %}{% if group.techadvisors %} Tech Advisor{{ group.techadvisors|pluralize }}: -{% for techadvisor in group.techadvisors %} {{ techadvisor.person.plain_name }} <{{ techadvisor.address }}> +{% for techadvisor in group.techadvisors %} {{ techadvisor.person.plain_name }} <{{ techadvisor.email.address }}> {% endfor %}{% endif %}{% if group.editors %} Editor{{ group.editors|pluralize }}: -{% for editor in group.editors %} {{ editor.person.plain_name }} <{{ editor.address}}> +{% for editor in group.editors %} {{ editor.person.plain_name }} <{{ editor.email.address }}> {% endfor %}{% endif %}{% if group.secretaries %} Secretar{{ group.secretaries|pluralize:"y,ies" }}: -{% for secretary in group.secretaries %} {{ secretary.person.plain_name }} <{{ secretary.address }}> +{% for secretary in group.secretaries %} {{ secretary.person.plain_name }} <{{ secretary.email.address }}> {% endfor %}{% endif %} Mailing Lists: General Discussion: {{ group.list_email }} diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 97bf5959a..a81fbf3fd 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -41,22 +41,22 @@ from django.conf import settings from django.core.urlresolvers import reverse as urlreverse from ietf.doc.views_search import SearchForm, retrieve_search_results -from ietf.ipr.models import IprDetail -from ietf.group.models import Group, GroupURL +from ietf.group.models import Group, GroupURL, Role from ietf.doc.models import State, DocAlias, RelatedDocument from ietf.doc.utils import get_chartering_type -from ietf.person.models import Email from ietf.group.utils import get_charter_text from ietf.doc.templatetags.ietf_filters import clean_whitespace from ietf.ietfauth.utils import has_role +def roles(group, role_name): + return Role.objects.filter(group=group, name=role_name).select_related("email", "person") def fill_in_charter_info(group, include_drafts=False): group.areadirector = group.ad.role_email("ad", group.parent) if group.ad else None - group.chairs = Email.objects.filter(role__group=group, role__name="chair").select_related("person") - group.techadvisors = Email.objects.filter(role__group=group, role__name="techadv").select_related("person") - group.editors = Email.objects.filter(role__group=group, role__name="editor").select_related("person") - group.secretaries = Email.objects.filter(role__group=group, role__name="secr").select_related("person") + group.chairs =roles(group, "chair") + group.techadvisors = roles(group, "techadv") + group.editors = roles(group, "editor") + group.secretaries = roles(group, "secr") milestone_state = "charter" if group.state_id == "proposed" else "active" group.milestones = group.groupmilestone_set.filter(state=milestone_state).order_by('due') @@ -77,19 +77,16 @@ def fill_in_charter_info(group, include_drafts=False): a.rel = RelatedDocument.objects.filter(source=a.document).distinct() a.invrel = RelatedDocument.objects.filter(target=a).distinct() -def extract_last_name(email): - return email.person.name_parts()[3] - -def extract_group_chairs(group): - return sorted(Email.objects.filter(role__group=group, role__name="chair").select_related("person"), key=extract_last_name) +def extract_last_name(role): + return role.person.name_parts()[3] def wg_summary_area(request): areas = Group.objects.filter(type="area", state="active").order_by("name") for area in areas: - area.ads = sorted(Email.objects.filter(role__group=area, role__name="ad").select_related("person"), key=extract_last_name) + area.ads = sorted(roles(area, "ad"), key=extract_last_name) area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") for group in area.groups: - group.chairs = extract_group_chairs(group) + group.chairs = sorted(roles(group, "chair"), key=extract_last_name) areas = [a for a in areas if a.groups] @@ -101,7 +98,7 @@ def wg_summary_acronym(request): areas = Group.objects.filter(type="area", state="active").order_by("name") groups = Group.objects.filter(type="wg", state="active").order_by("acronym").select_related("parent") for group in groups: - group.chairs = extract_group_chairs(group) + group.chairs = sorted(roles(group, "chair"), key=extract_last_name) return render_to_response('wginfo/1wg-summary-by-acronym.txt', { 'areas': areas, 'groups': groups }, @@ -110,7 +107,7 @@ def wg_summary_acronym(request): def wg_charters(request): areas = Group.objects.filter(type="area", state="active").order_by("name") for area in areas: - area.ads = sorted(Email.objects.filter(role__group=area, role__name="ad").select_related("person"), key=extract_last_name) + area.ads = sorted(roles(area, "ad"), key=extract_last_name) area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("name") for group in area.groups: fill_in_charter_info(group, include_drafts=True) @@ -123,7 +120,7 @@ def wg_charters_by_acronym(request): areas = dict((a.id, a) for a in Group.objects.filter(type="area", state="active").order_by("name")) for area in areas.itervalues(): - area.ads = sorted(Email.objects.filter(role__group=area, role__name="ad").select_related("person"), key=extract_last_name) + area.ads = sorted(roles(area, "ad"), key=extract_last_name) groups = Group.objects.filter(type="wg", state="active").exclude(parent=None).order_by("acronym") for group in groups: @@ -137,20 +134,13 @@ def active_wgs(request): areas = Group.objects.filter(type="area", state="active").order_by("name") for area in areas: # dig out information for template - area.ads = [] - for e in Email.objects.filter(role__group=area, role__name="ad").select_related("person"): - e.incoming = False - area.ads.append(e) + area.ads = (list(sorted(roles(area, "ad"), key=extract_last_name)) + + list(sorted(roles(area, "pre-ad"), key=extract_last_name))) - for e in Email.objects.filter(role__group=area, role__name="pre-ad").select_related("person"): - e.incoming = True - area.ads.append(e) - - area.ads.sort(key=lambda e: (e.incoming, extract_last_name(e))) - area.wgs = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") + area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") area.urls = area.groupurl_set.all().order_by("name") - for wg in area.wgs: - wg.chairs = extract_group_chairs(wg) + for group in area.groups: + group.chairs = sorted(roles(group, "chair"), key=extract_last_name) return render_to_response('wginfo/active_wgs.html', {'areas':areas}, RequestContext(request)) @@ -275,7 +265,7 @@ def group_charter(request, acronym): group = get_object_or_404(Group, type="wg", acronym=acronym) fill_in_charter_info(group, include_drafts=False) - group.delegates = Email.objects.filter(role__group=group, role__name="delegate").select_related("person") + group.delegates = roles(group, "delegate") e = group.latest_event(type__in=("changed_state", "requested_close",)) requested_close = group.state_id != "conclude" and e and e.type == "requested_close" From 37825f2e5e02e0728124d1289f68b0a031d613da Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sun, 29 Sep 2013 13:31:10 +0000 Subject: [PATCH 047/173] Save the test fixtures as JSON rather than XML, this seems to speed up the tests by 10-15% - Legacy-Id: 6302 --- ietf/name/fixtures/names.json | 3540 ++++++++++++++++++++++++++++++++ ietf/name/fixtures/names.xml | 2274 -------------------- ietf/name/generate_fixtures.py | 4 +- 3 files changed, 3542 insertions(+), 2276 deletions(-) create mode 100644 ietf/name/fixtures/names.json delete mode 100644 ietf/name/fixtures/names.xml diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json new file mode 100644 index 000000000..d87bfe154 --- /dev/null +++ b/ietf/name/fixtures/names.json @@ -0,0 +1,3540 @@ +[ + { + "pk": "yes", + "model": "name.ballotpositionname", + "fields": { + "order": 1, + "used": true, + "name": "Yes", + "blocking": false, + "desc": "" + } + }, + { + "pk": "noobj", + "model": "name.ballotpositionname", + "fields": { + "order": 2, + "used": true, + "name": "No Objection", + "blocking": false, + "desc": "" + } + }, + { + "pk": "discuss", + "model": "name.ballotpositionname", + "fields": { + "order": 3, + "used": true, + "name": "Discuss", + "blocking": true, + "desc": "" + } + }, + { + "pk": "block", + "model": "name.ballotpositionname", + "fields": { + "order": 3, + "used": true, + "name": "Block", + "blocking": true, + "desc": "" + } + }, + { + "pk": "abstain", + "model": "name.ballotpositionname", + "fields": { + "order": 4, + "used": true, + "name": "Abstain", + "blocking": false, + "desc": "" + } + }, + { + "pk": "recuse", + "model": "name.ballotpositionname", + "fields": { + "order": 5, + "used": true, + "name": "Recuse", + "blocking": false, + "desc": "" + } + }, + { + "pk": "norecord", + "model": "name.ballotpositionname", + "fields": { + "order": 6, + "used": true, + "name": "No Record", + "blocking": false, + "desc": "" + } + }, + { + "pk": "conflict", + "model": "name.constraintname", + "fields": { + "order": 0, + "used": true, + "name": "Conflicts with", + "desc": "" + } + }, + { + "pk": "conflic2", + "model": "name.constraintname", + "fields": { + "order": 0, + "used": true, + "name": "Conflicts with (secondary)", + "desc": "" + } + }, + { + "pk": "conflic3", + "model": "name.constraintname", + "fields": { + "order": 0, + "used": true, + "name": "Conflicts with (tertiary)", + "desc": "" + } + }, + { + "pk": "rst", + "model": "name.dbtemplatetypename", + "fields": { + "order": 0, + "used": true, + "name": "reStructuredText", + "desc": "" + } + }, + { + "pk": "plain", + "model": "name.dbtemplatetypename", + "fields": { + "order": 0, + "used": true, + "name": "Plain", + "desc": "" + } + }, + { + "pk": "django", + "model": "name.dbtemplatetypename", + "fields": { + "order": 0, + "used": true, + "name": "Django", + "desc": "" + } + }, + { + "pk": "obs", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Obsoleted by", + "used": true, + "name": "Obsoletes", + "desc": "" + } + }, + { + "pk": "updates", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Updated by", + "used": true, + "name": "Updates", + "desc": "" + } + }, + { + "pk": "replaces", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Replaced by", + "used": true, + "name": "Replaces", + "desc": "" + } + }, + { + "pk": "conflrev", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Conflict reviewed by", + "used": true, + "name": "conflict reviews", + "desc": "" + } + }, + { + "pk": "refnorm", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "fixme", + "used": true, + "name": "Normative Reference", + "desc": "Normative Reference" + } + }, + { + "pk": "refold", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "fixme", + "used": true, + "name": "Reference", + "desc": "A reference found in a document which does not have split normative/informative reference sections." + } + }, + { + "pk": "refinfo", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "fixme", + "used": true, + "name": "Informative Reference", + "desc": "Informative Reference" + } + }, + { + "pk": "tops", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Proposed Standard by", + "used": true, + "name": "Moves to Proposed Standard", + "desc": "" + } + }, + { + "pk": "tois", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Internet Standard by", + "used": true, + "name": "Moves to Internet Standard", + "desc": "" + } + }, + { + "pk": "tohist", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Historic by", + "used": true, + "name": "Moves to Historic", + "desc": "" + } + }, + { + "pk": "toinf", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Informational by", + "used": true, + "name": "Moves to Informational", + "desc": "" + } + }, + { + "pk": "tobcp", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to BCP by", + "used": true, + "name": "Moves to BCP", + "desc": "" + } + }, + { + "pk": "toexp", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Experimental by", + "used": true, + "name": "Moves to Experimental", + "desc": "" + } + }, + { + "pk": "refunk", + "model": "name.docrelationshipname", + "fields": { + "order": 3, + "revname": "fixme", + "used": true, + "name": "Possible Reference", + "desc": "Reference of unknown type, likely found in the text of the document." + } + }, + { + "pk": "stream-s", + "model": "name.docremindertypename", + "fields": { + "order": 0, + "used": true, + "name": "Stream state should change", + "desc": "" + } + }, + { + "pk": "iana-crd", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "IANA coordination", + "desc": "RFC-Editor/IANA Registration Coordination" + } + }, + { + "pk": "ref", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Holding for references", + "desc": "Holding for normative reference" + } + }, + { + "pk": "missref", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Missing references", + "desc": "Awaiting missing normative reference" + } + }, + { + "pk": "errata", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Has errata", + "desc": "" + } + }, + { + "pk": "rfc-rev", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Review by RFC Editor", + "desc": "" + } + }, + { + "pk": "via-rfc", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Via RFC Editor", + "desc": "" + } + }, + { + "pk": "app-min", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Approved in minutes", + "desc": "" + } + }, + { + "pk": "need-sh", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Shepherd Needed", + "desc": "" + } + }, + { + "pk": "w-dep", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Waiting for Dependency on Other Document", + "desc": "" + } + }, + { + "pk": "iesg-com", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "IESG Review Completed", + "desc": "" + } + }, + { + "pk": "iana", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "IANA", + "desc": "The document has IANA actions that are not yet completed." + } + }, + { + "pk": "rev-wg", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Revised I-D Needed - Issue raised by WG", + "desc": "" + } + }, + { + "pk": "point", + "model": "name.doctagname", + "fields": { + "order": 1, + "used": true, + "name": "Point Raised - writeup needed", + "desc": "IESG discussions on the document have raised some issues that need to be brought to the attention of the authors/WG, but those issues have not been written down yet. (It is common for discussions during a telechat to result in such situations. An AD may raise a possible issue during a telechat and only decide as a result of that discussion whether the issue is worth formally writing up and bringing to the attention of the authors/WG). A document stays in the \"Point Raised - Writeup Needed\" state until *ALL* IESG comments that have been raised have been documented." + } + }, + { + "pk": "w-expert", + "model": "name.doctagname", + "fields": { + "order": 1, + "used": true, + "name": "Awaiting Expert Review/Resolution of Issues Raised", + "desc": "" + } + }, + { + "pk": "need-ed", + "model": "name.doctagname", + "fields": { + "order": 1, + "used": true, + "name": "Editor Needed", + "desc": "" + } + }, + { + "pk": "ad-f-up", + "model": "name.doctagname", + "fields": { + "order": 2, + "used": true, + "name": "AD Followup", + "desc": "A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include:\n\n- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG.\n\n- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently.\n\n- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes." + } + }, + { + "pk": "w-extern", + "model": "name.doctagname", + "fields": { + "order": 2, + "used": true, + "name": "Awaiting External Review/Resolution of Issues Raised", + "desc": "" + } + }, + { + "pk": "w-part", + "model": "name.doctagname", + "fields": { + "order": 2, + "used": true, + "name": "Waiting for Partner Feedback", + "desc": "" + } + }, + { + "pk": "extpty", + "model": "name.doctagname", + "fields": { + "order": 3, + "used": true, + "name": "External Party", + "desc": "The document is awaiting review or input from an external party (i.e, someone other than the shepherding AD, the authors, or the WG). See the \"note\" field for more details on who has the action." + } + }, + { + "pk": "w-merge", + "model": "name.doctagname", + "fields": { + "order": 3, + "used": true, + "name": "Awaiting Merge with Other Document", + "desc": "" + } + }, + { + "pk": "w-review", + "model": "name.doctagname", + "fields": { + "order": 3, + "used": true, + "name": "Awaiting Reviews", + "desc": "" + } + }, + { + "pk": "need-aut", + "model": "name.doctagname", + "fields": { + "order": 4, + "used": true, + "name": "Author or Editor Needed", + "desc": "" + } + }, + { + "pk": "sh-f-up", + "model": "name.doctagname", + "fields": { + "order": 4, + "used": true, + "name": "Document Shepherd Followup", + "desc": "" + } + }, + { + "pk": "need-rev", + "model": "name.doctagname", + "fields": { + "order": 5, + "used": true, + "name": "Revised I-D Needed", + "desc": "An updated I-D is needed to address the issues that have been raised." + } + }, + { + "pk": "w-refdoc", + "model": "name.doctagname", + "fields": { + "order": 5, + "used": true, + "name": "Waiting for Referenced Document", + "desc": "" + } + }, + { + "pk": "w-refing", + "model": "name.doctagname", + "fields": { + "order": 6, + "used": true, + "name": "Waiting for Referencing Document", + "desc": "" + } + }, + { + "pk": "rev-wglc", + "model": "name.doctagname", + "fields": { + "order": 7, + "used": true, + "name": "Revised I-D Needed - Issue raised by WGLC", + "desc": "" + } + }, + { + "pk": "rev-ad", + "model": "name.doctagname", + "fields": { + "order": 8, + "used": true, + "name": "Revised I-D Needed - Issue raised by AD", + "desc": "" + } + }, + { + "pk": "rev-iesg", + "model": "name.doctagname", + "fields": { + "order": 9, + "used": true, + "name": "Revised I-D Needed - Issue raised by IESG", + "desc": "" + } + }, + { + "pk": "sheph-u", + "model": "name.doctagname", + "fields": { + "order": 10, + "used": true, + "name": "Doc Shepherd Follow-up Underway", + "desc": "" + } + }, + { + "pk": "other", + "model": "name.doctagname", + "fields": { + "order": 11, + "used": true, + "name": "Other - see Comment Log", + "desc": "" + } + }, + { + "pk": "charter", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Charter", + "desc": "" + } + }, + { + "pk": "agenda", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Agenda", + "desc": "" + } + }, + { + "pk": "minutes", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Minutes", + "desc": "" + } + }, + { + "pk": "slides", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Slides", + "desc": "" + } + }, + { + "pk": "draft", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Draft", + "desc": "" + } + }, + { + "pk": "liai-att", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Liaison Attachment", + "desc": "" + } + }, + { + "pk": "conflrev", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Conflict Review", + "desc": "" + } + }, + { + "pk": "statchg", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Status Change", + "desc": "" + } + }, + { + "pk": "shepwrit", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": false, + "name": "Shepherd's writeup", + "desc": "" + } + }, + { + "pk": "active", + "model": "name.groupmilestonestatename", + "fields": { + "order": 1, + "used": true, + "name": "Active", + "desc": "" + } + }, + { + "pk": "deleted", + "model": "name.groupmilestonestatename", + "fields": { + "order": 2, + "used": true, + "name": "Deleted", + "desc": "" + } + }, + { + "pk": "review", + "model": "name.groupmilestonestatename", + "fields": { + "order": 3, + "used": true, + "name": "For review", + "desc": "" + } + }, + { + "pk": "charter", + "model": "name.groupmilestonestatename", + "fields": { + "order": 4, + "used": true, + "name": "Chartering/rechartering", + "desc": "" + } + }, + { + "pk": "bof", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "BOF", + "desc": "" + } + }, + { + "pk": "proposed", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Proposed", + "desc": "" + } + }, + { + "pk": "active", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Active", + "desc": "" + } + }, + { + "pk": "dormant", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Dormant", + "desc": "" + } + }, + { + "pk": "conclude", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Concluded", + "desc": "" + } + }, + { + "pk": "unknown", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Unknown", + "desc": "" + } + }, + { + "pk": "abandon", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Abandonded", + "desc": "Formation of the group (most likely a BoF or Proposed WG) was abandoned" + } + }, + { + "pk": "bof-conc", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "BOF Concluded", + "desc": "" + } + }, + { + "pk": "ietf", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "IETF", + "desc": "" + } + }, + { + "pk": "area", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Area", + "desc": "" + } + }, + { + "pk": "ag", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "AG", + "desc": "Area group" + } + }, + { + "pk": "wg", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "WG", + "desc": "Working group" + } + }, + { + "pk": "rg", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "RG", + "desc": "Research group" + } + }, + { + "pk": "team", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Team", + "desc": "" + } + }, + { + "pk": "individ", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Individual", + "desc": "" + } + }, + { + "pk": "sdo", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "SDO", + "desc": "Standards organization" + } + }, + { + "pk": "irtf", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "IRTF", + "desc": "" + } + }, + { + "pk": "rfcedtyp", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "RFC Editor", + "desc": "" + } + }, + { + "pk": "ps", + "model": "name.intendedstdlevelname", + "fields": { + "order": 1, + "used": true, + "name": "Proposed Standard", + "desc": "" + } + }, + { + "pk": "ds", + "model": "name.intendedstdlevelname", + "fields": { + "order": 2, + "used": false, + "name": "Draft Standard", + "desc": "" + } + }, + { + "pk": "std", + "model": "name.intendedstdlevelname", + "fields": { + "order": 3, + "used": true, + "name": "Internet Standard", + "desc": "" + } + }, + { + "pk": "bcp", + "model": "name.intendedstdlevelname", + "fields": { + "order": 4, + "used": true, + "name": "Best Current Practice", + "desc": "" + } + }, + { + "pk": "inf", + "model": "name.intendedstdlevelname", + "fields": { + "order": 5, + "used": true, + "name": "Informational", + "desc": "" + } + }, + { + "pk": "exp", + "model": "name.intendedstdlevelname", + "fields": { + "order": 6, + "used": true, + "name": "Experimental", + "desc": "" + } + }, + { + "pk": "hist", + "model": "name.intendedstdlevelname", + "fields": { + "order": 7, + "used": true, + "name": "Historic", + "desc": "" + } + }, + { + "pk": "action", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 1, + "used": true, + "name": "For action", + "desc": "" + } + }, + { + "pk": "comment", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 2, + "used": true, + "name": "For comment", + "desc": "" + } + }, + { + "pk": "info", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 3, + "used": true, + "name": "For information", + "desc": "" + } + }, + { + "pk": "response", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 4, + "used": true, + "name": "In response", + "desc": "" + } + }, + { + "pk": "ietf", + "model": "name.meetingtypename", + "fields": { + "order": 0, + "used": true, + "name": "IETF", + "desc": "" + } + }, + { + "pk": "interim", + "model": "name.meetingtypename", + "fields": { + "order": 0, + "used": true, + "name": "Interim", + "desc": "" + } + }, + { + "pk": "ad", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Area Director", + "desc": "" + } + }, + { + "pk": "pre-ad", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Incoming Area Director", + "desc": "" + } + }, + { + "pk": "chair", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Chair", + "desc": "" + } + }, + { + "pk": "editor", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Editor", + "desc": "" + } + }, + { + "pk": "secr", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Secretary", + "desc": "" + } + }, + { + "pk": "techadv", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Tech Advisor", + "desc": "" + } + }, + { + "pk": "execdir", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Executive Director", + "desc": "" + } + }, + { + "pk": "admdir", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Administrative Director", + "desc": "" + } + }, + { + "pk": "liaiman", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Liaison Manager", + "desc": "" + } + }, + { + "pk": "auth", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Authorized Individual", + "desc": "" + } + }, + { + "pk": "delegate", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Delegate", + "desc": "" + } + }, + { + "pk": "atlarge", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "At Large Member", + "desc": "" + } + }, + { + "pk": "member", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Member", + "desc": "Regular group member in a group that has explicit membership, such as the NomCom" + } + }, + { + "pk": "liaison", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Liaison Member", + "desc": "Liaison group member in a group that has explicit membership, such as the NomCom" + } + }, + { + "pk": "advisor", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Advisor", + "desc": "Advisor in a group that has explicit membership, such as the NomCom" + } + }, + { + "pk": "schedw", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Waiting for Scheduling", + "desc": "" + } + }, + { + "pk": "apprw", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Waiting for Approval", + "desc": "" + } + }, + { + "pk": "appr", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Approved", + "desc": "" + } + }, + { + "pk": "sched", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Scheduled", + "desc": "" + } + }, + { + "pk": "canceled", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Canceled", + "desc": "" + } + }, + { + "pk": "disappr", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Disapproved", + "desc": "" + } + }, + { + "pk": "notmeet", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Not meeting", + "desc": "" + } + }, + { + "pk": "deleted", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Deleted", + "desc": "" + } + }, + { + "pk": "std", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Internet Standard", + "desc": "" + } + }, + { + "pk": "ds", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": false, + "name": "Draft Standard", + "desc": "" + } + }, + { + "pk": "ps", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Proposed Standard", + "desc": "" + } + }, + { + "pk": "inf", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Informational", + "desc": "" + } + }, + { + "pk": "exp", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Experimental", + "desc": "" + } + }, + { + "pk": "bcp", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Best Current Practice", + "desc": "" + } + }, + { + "pk": "hist", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Historic", + "desc": "" + } + }, + { + "pk": "unkn", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Unknown", + "desc": "" + } + }, + { + "pk": "ietf", + "model": "name.streamname", + "fields": { + "order": 1, + "used": true, + "name": "IETF", + "desc": "IETF stream" + } + }, + { + "pk": "ise", + "model": "name.streamname", + "fields": { + "order": 2, + "used": true, + "name": "ISE", + "desc": "Independent Submission Editor stream" + } + }, + { + "pk": "irtf", + "model": "name.streamname", + "fields": { + "order": 3, + "used": true, + "name": "IRTF", + "desc": "Independent Submission Editor stream" + } + }, + { + "pk": "iab", + "model": "name.streamname", + "fields": { + "order": 4, + "used": true, + "name": "IAB", + "desc": "IAB stream" + } + }, + { + "pk": "legacy", + "model": "name.streamname", + "fields": { + "order": 5, + "used": true, + "name": "Legacy", + "desc": "Legacy stream" + } + }, + { + "pk": "other", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Other", + "desc": "" + } + }, + { + "pk": "session", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Session", + "desc": "" + } + }, + { + "pk": "break", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Break", + "desc": "" + } + }, + { + "pk": "reg", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Registration", + "desc": "" + } + }, + { + "pk": "plenary", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Plenary", + "desc": "" + } + }, + { + "pk": "draft", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "draft-iesg", + "model": "doc.statetype", + "fields": { + "label": "IESG state" + } + }, + { + "pk": "draft-iana", + "model": "doc.statetype", + "fields": { + "label": "IANA state" + } + }, + { + "pk": "draft-rfceditor", + "model": "doc.statetype", + "fields": { + "label": "RFC Editor state" + } + }, + { + "pk": "draft-stream-ietf", + "model": "doc.statetype", + "fields": { + "label": "IETF WG state" + } + }, + { + "pk": "draft-stream-irtf", + "model": "doc.statetype", + "fields": { + "label": "IRTF state" + } + }, + { + "pk": "draft-stream-ise", + "model": "doc.statetype", + "fields": { + "label": "ISE state" + } + }, + { + "pk": "draft-stream-iab", + "model": "doc.statetype", + "fields": { + "label": "IAB state" + } + }, + { + "pk": "slides", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "minutes", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "agenda", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "liai-att", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "charter", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "conflrev", + "model": "doc.statetype", + "fields": { + "label": "Conflict Review State" + } + }, + { + "pk": "draft-iana-action", + "model": "doc.statetype", + "fields": { + "label": "IANA Action state" + } + }, + { + "pk": "draft-iana-review", + "model": "doc.statetype", + "fields": { + "label": "IANA Review state" + } + }, + { + "pk": "statchg", + "model": "doc.statetype", + "fields": { + "label": "RFC Status Change" + } + }, + { + "pk": 81, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "agenda", + "order": 1, + "desc": "" + } + }, + { + "pk": 82, + "model": "doc.state", + "fields": { + "used": true, + "name": "Deleted", + "next_states": [], + "slug": "deleted", + "type": "agenda", + "order": 2, + "desc": "" + } + }, + { + "pk": 83, + "model": "doc.state", + "fields": { + "used": true, + "name": "Not currently under review", + "next_states": [], + "slug": "notrev", + "type": "charter", + "order": 0, + "desc": "The proposed charter is not being considered at this time. A proposed charter will remain in this state until an AD moves it to Informal IESG review." + } + }, + { + "pk": 84, + "model": "doc.state", + "fields": { + "used": true, + "name": "Informal IESG review", + "next_states": [], + "slug": "infrev", + "type": "charter", + "order": 0, + "desc": "This is the initial state when an AD proposes a new charter. The normal next state is Internal review if the idea is accepted, or Not currently under review if the idea is abandoned." + } + }, + { + "pk": 85, + "model": "doc.state", + "fields": { + "used": true, + "name": "Internal review", + "next_states": [], + "slug": "intrev", + "type": "charter", + "order": 0, + "desc": "The IESG and IAB are reviewing the early draft of the charter; this is the initial IESG and IAB review. The usual next state is External review if the idea is adopted, or Informal IESG review if the IESG decides the idea needs more work, or Not currently under review is the idea is abandoned" + } + }, + { + "pk": 86, + "model": "doc.state", + "fields": { + "used": true, + "name": "External review", + "next_states": [], + "slug": "extrev", + "type": "charter", + "order": 0, + "desc": "The IETF community and possibly other standards development organizations (SDOs) are reviewing the proposed charter. The usual next state is IESG review, although it might move to Not currently under review is the idea is abandoned during the external review." + } + }, + { + "pk": 87, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG review", + "next_states": [], + "slug": "iesgrev", + "type": "charter", + "order": 0, + "desc": "The IESG is reviewing the discussion from the external review of the proposed charter. The usual next state is Approved, or Not currently under review if the idea is abandoned." + } + }, + { + "pk": 88, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved", + "next_states": [], + "slug": "approved", + "type": "charter", + "order": 0, + "desc": "The charter is approved by the IESG." + } + }, + { + "pk": 90, + "model": "doc.state", + "fields": { + "used": true, + "name": "Needs Shepherd", + "next_states": [ + 91, + 98, + 99 + ], + "slug": "needshep", + "type": "conflrev", + "order": 1, + "desc": "A conflict review has been requested, but a shepherding AD has not yet been assigned" + } + }, + { + "pk": 91, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD Review", + "next_states": [ + 92, + 98, + 99 + ], + "slug": "adrev", + "type": "conflrev", + "order": 2, + "desc": "The sponsoring AD is reviewing the document and preparing a proposed response" + } + }, + { + "pk": 92, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation", + "next_states": [ + 93, + 94, + 95, + 98, + 99 + ], + "slug": "iesgeval", + "type": "conflrev", + "order": 3, + "desc": "The IESG is considering the proposed conflict review response" + } + }, + { + "pk": 93, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation - Defer", + "next_states": [ + 92, + 94, + 95, + 98, + 99 + ], + "slug": "defer", + "type": "conflrev", + "order": 4, + "desc": "The evaluation of the proposed conflict review response has been deferred to the next telechat" + } + }, + { + "pk": 100, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved Request to Not Publish - point raised", + "next_states": [ + 94 + ], + "slug": "appr-reqnopub-pr", + "type": "conflrev", + "order": 5, + "desc": "The IESG has approved the conflict review response (a request to not publish), but a point has been raised that should be cleared before moving to announcement to be sent" + } + }, + { + "pk": 101, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved No Problem - point raised", + "next_states": [ + 95 + ], + "slug": "appr-noprob-pr", + "type": "conflrev", + "order": 6, + "desc": "The IESG has approved the conflict review response, but a point has been raised that should be cleared before proceeding to announcement to be sent" + } + }, + { + "pk": 94, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved Request to Not Publish - announcement to be sent", + "next_states": [ + 96, + 98 + ], + "slug": "appr-reqnopub-pend", + "type": "conflrev", + "order": 7, + "desc": "The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response" + } + }, + { + "pk": 95, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved No Problem - announcement to be sent", + "next_states": [ + 97, + 98 + ], + "slug": "appr-noprob-pend", + "type": "conflrev", + "order": 8, + "desc": "The IESG has approved the conflict review response, but the secretariat has not yet sent the response" + } + }, + { + "pk": 96, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved Request to Not Publish - announcement sent", + "next_states": [ + 96 + ], + "slug": "appr-reqnopub-sent", + "type": "conflrev", + "order": 9, + "desc": "The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester" + } + }, + { + "pk": 97, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved No Problem - announcement sent", + "next_states": [ + 97 + ], + "slug": "appr-noprob-sent", + "type": "conflrev", + "order": 10, + "desc": "The secretariat has delivered the IESG's approved conflict review response to the requester" + } + }, + { + "pk": 98, + "model": "doc.state", + "fields": { + "used": true, + "name": "Withdrawn", + "next_states": [ + 90 + ], + "slug": "withdraw", + "type": "conflrev", + "order": 11, + "desc": "The request for conflict review was withdrawn" + } + }, + { + "pk": 99, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead", + "next_states": [ + 90 + ], + "slug": "dead", + "type": "conflrev", + "order": 12, + "desc": "The conflict review has been abandoned" + } + }, + { + "pk": 1, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "draft", + "order": 1, + "desc": "" + } + }, + { + "pk": 2, + "model": "doc.state", + "fields": { + "used": true, + "name": "Expired", + "next_states": [], + "slug": "expired", + "type": "draft", + "order": 2, + "desc": "" + } + }, + { + "pk": 3, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC", + "next_states": [], + "slug": "rfc", + "type": "draft", + "order": 3, + "desc": "" + } + }, + { + "pk": 4, + "model": "doc.state", + "fields": { + "used": true, + "name": "Replaced", + "next_states": [], + "slug": "repl", + "type": "draft", + "order": 4, + "desc": "" + } + }, + { + "pk": 5, + "model": "doc.state", + "fields": { + "used": true, + "name": "Withdrawn by Submitter", + "next_states": [], + "slug": "auth-rm", + "type": "draft", + "order": 5, + "desc": "" + } + }, + { + "pk": 6, + "model": "doc.state", + "fields": { + "used": true, + "name": "Withdrawn by IETF", + "next_states": [], + "slug": "ietf-rm", + "type": "draft", + "order": 6, + "desc": "" + } + }, + { + "pk": 102, + "model": "doc.state", + "fields": { + "used": true, + "name": "New Document", + "next_states": [], + "slug": "newdoc", + "type": "draft-iana-action", + "order": 1, + "desc": "A new document has been received by IANA, but no actions have been taken" + } + }, + { + "pk": 103, + "model": "doc.state", + "fields": { + "used": true, + "name": "In Progress", + "next_states": [], + "slug": "inprog", + "type": "draft-iana-action", + "order": 2, + "desc": "IANA is currently processing the actions for this document" + } + }, + { + "pk": 104, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on Authors", + "next_states": [], + "slug": "waitauth", + "type": "draft-iana-action", + "order": 3, + "desc": "IANA is waiting on the document's authors to respond" + } + }, + { + "pk": 105, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on ADs", + "next_states": [], + "slug": "waitad", + "type": "draft-iana-action", + "order": 4, + "desc": "IANA is waiting on the IETF Area Directors to respond" + } + }, + { + "pk": 106, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on WGC", + "next_states": [], + "slug": "waitwgc", + "type": "draft-iana-action", + "order": 5, + "desc": "IANA is waiting on the IETF Working Group Chairs to respond" + } + }, + { + "pk": 107, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on RFC Editor", + "next_states": [], + "slug": "waitrfc", + "type": "draft-iana-action", + "order": 6, + "desc": "IANA has notified the RFC Editor that the actions have been completed" + } + }, + { + "pk": 108, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC-Ed-Ack", + "next_states": [], + "slug": "rfcedack", + "type": "draft-iana-action", + "order": 7, + "desc": "Request completed. The RFC Editor has acknowledged receipt of IANA's message that the actions have been completed" + } + }, + { + "pk": 109, + "model": "doc.state", + "fields": { + "used": true, + "name": "On Hold", + "next_states": [], + "slug": "onhold", + "type": "draft-iana-action", + "order": 8, + "desc": "IANA has suspended work on the document" + } + }, + { + "pk": 110, + "model": "doc.state", + "fields": { + "used": true, + "name": "No IC", + "next_states": [], + "slug": "noic", + "type": "draft-iana-action", + "order": 9, + "desc": "Request completed. There were no IANA actions for this document" + } + }, + { + "pk": 111, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA - Review Needed", + "next_states": [], + "slug": "need-rev", + "type": "draft-iana-review", + "order": 1, + "desc": "Document has not yet been reviewed by IANA." + } + }, + { + "pk": 112, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA OK - Actions Needed", + "next_states": [], + "slug": "ok-act", + "type": "draft-iana-review", + "order": 2, + "desc": "Document requires IANA actions, and the IANA Considerations section indicates the details of the actions correctly." + } + }, + { + "pk": 113, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA OK - No Actions Needed", + "next_states": [], + "slug": "ok-noact", + "type": "draft-iana-review", + "order": 3, + "desc": "Document requires no IANA action, and the IANA Considerations section indicates this correctly." + } + }, + { + "pk": 114, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA - Not OK", + "next_states": [], + "slug": "not-ok", + "type": "draft-iana-review", + "order": 4, + "desc": "IANA has issues with the text of the IANA Considerations section of the document." + } + }, + { + "pk": 115, + "model": "doc.state", + "fields": { + "used": true, + "name": "Version Changed - Review Needed", + "next_states": [], + "slug": "changed", + "type": "draft-iana-review", + "order": 5, + "desc": "Document revision has changed after review by IANA." + } + }, + { + "pk": 16, + "model": "doc.state", + "fields": { + "used": true, + "name": "Publication Requested", + "next_states": [ + 13, + 11, + 8 + ], + "slug": "pub-req", + "type": "draft-iesg", + "order": 10, + "desc": "A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested." + } + }, + { + "pk": 13, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD Evaluation", + "next_states": [ + 21, + 14, + 12, + 11 + ], + "slug": "ad-eval", + "type": "draft-iesg", + "order": 11, + "desc": "A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole." + } + }, + { + "pk": 21, + "model": "doc.state", + "fields": { + "used": true, + "name": "Expert Review", + "next_states": [ + 13 + ], + "slug": "review-e", + "type": "draft-iesg", + "order": 12, + "desc": "An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the \"MIB doctors\". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the \"note\" field for specific details on the nature of the review." + } + }, + { + "pk": 14, + "model": "doc.state", + "fields": { + "used": true, + "name": "Last Call Requested", + "next_states": [ + 15 + ], + "slug": "lc-req", + "type": "draft-iesg", + "order": 15, + "desc": "The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet." + } + }, + { + "pk": 15, + "model": "doc.state", + "fields": { + "used": true, + "name": "In Last Call", + "next_states": [ + 19, + 20 + ], + "slug": "lc", + "type": "draft-iesg", + "order": 16, + "desc": "The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks." + } + }, + { + "pk": 19, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for Writeup", + "next_states": [ + 20 + ], + "slug": "writeupw", + "type": "draft-iesg", + "order": 18, + "desc": "Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC." + } + }, + { + "pk": 20, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for AD Go-Ahead", + "next_states": [ + 12 + ], + "slug": "goaheadw", + "type": "draft-iesg", + "order": 19, + "desc": "As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole." + } + }, + { + "pk": 12, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation", + "next_states": [ + 18, + 9, + 22 + ], + "slug": "iesg-eva", + "type": "draft-iesg", + "order": 20, + "desc": "The document is now (finally!) being formally reviewed by the entire IESG. Documents are discussed in email or during a bi-weekly IESG telechat. In this phase, each AD reviews the document and airs any issues they may have. Unresolvable issues are documented as \"discuss\" comments that can be forwarded to the authors/WG. See the description of substates for additional details about the current state of the IESG discussion." + } + }, + { + "pk": 18, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation - Defer", + "next_states": [ + 12 + ], + "slug": "defer", + "type": "draft-iesg", + "order": 21, + "desc": "During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat." + } + }, + { + "pk": 9, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved-announcement to be sent", + "next_states": [ + 10 + ], + "slug": "approved", + "type": "draft-iesg", + "order": 27, + "desc": "The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message." + } + }, + { + "pk": 10, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved-announcement sent", + "next_states": [ + 17 + ], + "slug": "ann", + "type": "draft-iesg", + "order": 30, + "desc": "The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor." + } + }, + { + "pk": 17, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC Ed Queue", + "next_states": [ + 7 + ], + "slug": "rfcqueue", + "type": "draft-iesg", + "order": 31, + "desc": "The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html)." + } + }, + { + "pk": 7, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC Published", + "next_states": [ + 8 + ], + "slug": "pub", + "type": "draft-iesg", + "order": 32, + "desc": "The ID has been published as an RFC." + } + }, + { + "pk": 22, + "model": "doc.state", + "fields": { + "used": true, + "name": "DNP-waiting for AD note", + "next_states": [ + 23 + ], + "slug": "nopubadw", + "type": "draft-iesg", + "order": 33, + "desc": "Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the \"note\" field for more details on who has the action item." + } + }, + { + "pk": 23, + "model": "doc.state", + "fields": { + "used": true, + "name": "DNP-announcement to be sent", + "next_states": [ + 8 + ], + "slug": "nopubanw", + "type": "draft-iesg", + "order": 34, + "desc": "The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official \"do not publish\" recommendation message." + } + }, + { + "pk": 11, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD is watching", + "next_states": [ + 16 + ], + "slug": "watching", + "type": "draft-iesg", + "order": 42, + "desc": "An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and \"I-D Exists\" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD's own reasons)." + } + }, + { + "pk": 8, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead", + "next_states": [ + 16 + ], + "slug": "dead", + "type": "draft-iesg", + "order": 99, + "desc": "Document is \"dead\" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.)" + } + }, + { + "pk": 24, + "model": "doc.state", + "fields": { + "used": true, + "name": "AUTH", + "next_states": [], + "slug": "auth", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting author action" + } + }, + { + "pk": 25, + "model": "doc.state", + "fields": { + "used": true, + "name": "AUTH48", + "next_states": [], + "slug": "auth48", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting final author approval" + } + }, + { + "pk": 26, + "model": "doc.state", + "fields": { + "used": false, + "name": "EDIT", + "next_states": [], + "slug": "edit", + "type": "draft-rfceditor", + "order": 0, + "desc": "Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing" + } + }, + { + "pk": 27, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA", + "next_states": [], + "slug": "iana", + "type": "draft-rfceditor", + "order": 0, + "desc": "Document has been edited, but is holding for completion of IANA actions" + } + }, + { + "pk": 28, + "model": "doc.state", + "fields": { + "used": false, + "name": "IESG", + "next_states": [], + "slug": "iesg", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting IESG action" + } + }, + { + "pk": 29, + "model": "doc.state", + "fields": { + "used": true, + "name": "ISR", + "next_states": [], + "slug": "isr", + "type": "draft-rfceditor", + "order": 0, + "desc": "Independent Submission Review by the ISE " + } + }, + { + "pk": 30, + "model": "doc.state", + "fields": { + "used": false, + "name": "ISR-AUTH", + "next_states": [], + "slug": "isr-auth", + "type": "draft-rfceditor", + "order": 0, + "desc": "Independent submission awaiting author action, or in discussion between author and ISE" + } + }, + { + "pk": 31, + "model": "doc.state", + "fields": { + "used": true, + "name": "REF", + "next_states": [], + "slug": "ref", + "type": "draft-rfceditor", + "order": 0, + "desc": "Holding for normative reference" + } + }, + { + "pk": 32, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC-EDITOR", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting final RFC Editor review before AUTH48" + } + }, + { + "pk": 33, + "model": "doc.state", + "fields": { + "used": true, + "name": "TO", + "next_states": [], + "slug": "timeout", + "type": "draft-rfceditor", + "order": 0, + "desc": "Time-out period during which the IESG reviews document for conflict/concurrence with other IETF working group work" + } + }, + { + "pk": 34, + "model": "doc.state", + "fields": { + "used": true, + "name": "MISSREF", + "next_states": [], + "slug": "missref", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting missing normative reference" + } + }, + { + "pk": 89, + "model": "doc.state", + "fields": { + "used": false, + "name": "AUTH48-DONE", + "next_states": [ + 74 + ], + "slug": "auth48done", + "type": "draft-rfceditor", + "order": 0, + "desc": "Final approvals are complete" + } + }, + { + "pk": 116, + "model": "doc.state", + "fields": { + "used": true, + "name": "AUTH48-DONE", + "next_states": [], + "slug": "auth48-done", + "type": "draft-rfceditor", + "order": 0, + "desc": "Final approvals are complete" + } + }, + { + "pk": 117, + "model": "doc.state", + "fields": { + "used": true, + "name": "EDIT", + "next_states": [], + "slug": "edit", + "type": "draft-rfceditor", + "order": 0, + "desc": "Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing" + } + }, + { + "pk": 118, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA", + "next_states": [], + "slug": "iana-crd", + "type": "draft-rfceditor", + "order": 0, + "desc": "RFC-Editor/IANA Registration Coordination" + } + }, + { + "pk": 119, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG", + "next_states": [], + "slug": "iesg", + "type": "draft-rfceditor", + "order": 0, + "desc": "Holding for IESG action" + } + }, + { + "pk": 120, + "model": "doc.state", + "fields": { + "used": true, + "name": "ISR-AUTH", + "next_states": [], + "slug": "isr-auth", + "type": "draft-rfceditor", + "order": 0, + "desc": "Independent Submission awaiting author update, or in discussion between author and ISE" + } + }, + { + "pk": 45, + "model": "doc.state", + "fields": { + "used": true, + "name": "Candidate IAB Document", + "next_states": [], + "slug": "candidat", + "type": "draft-stream-iab", + "order": 1, + "desc": "A document being considered for the IAB stream." + } + }, + { + "pk": 46, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active IAB Document", + "next_states": [], + "slug": "active", + "type": "draft-stream-iab", + "order": 2, + "desc": "This document has been adopted by the IAB and is being actively developed." + } + }, + { + "pk": 47, + "model": "doc.state", + "fields": { + "used": true, + "name": "Parked IAB Document", + "next_states": [], + "slug": "parked", + "type": "draft-stream-iab", + "order": 3, + "desc": "This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the IAB for some other reason. Annotations probably explain why this document is parked." + } + }, + { + "pk": 48, + "model": "doc.state", + "fields": { + "used": true, + "name": "IAB Review", + "next_states": [], + "slug": "review-i", + "type": "draft-stream-iab", + "order": 4, + "desc": "This document is awaiting the IAB itself to come to internal consensus." + } + }, + { + "pk": 49, + "model": "doc.state", + "fields": { + "used": true, + "name": "Community Review", + "next_states": [], + "slug": "review-c", + "type": "draft-stream-iab", + "order": 5, + "desc": "This document has completed internal consensus within the IAB and is now under community review." + } + }, + { + "pk": 50, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved by IAB, To Be Sent to RFC Editor", + "next_states": [], + "slug": "approved", + "type": "draft-stream-iab", + "order": 6, + "desc": "The consideration of this document is complete, but it has not yet been sent to the RFC Editor for publication (although that is going to happen soon)." + } + }, + { + "pk": 51, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to a Different Organization for Publication", + "next_states": [], + "slug": "diff-org", + "type": "draft-stream-iab", + "order": 7, + "desc": "The IAB does not expect to publish the document itself, but has passed it on to a different organization that might continue work on the document. The expectation is that the other organization will eventually publish the document." + } + }, + { + "pk": 52, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to the RFC Editor", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-stream-iab", + "order": 8, + "desc": "The IAB processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the IAB." + } + }, + { + "pk": 53, + "model": "doc.state", + "fields": { + "used": true, + "name": "Published RFC", + "next_states": [], + "slug": "pub", + "type": "draft-stream-iab", + "order": 9, + "desc": "The document has been published as an RFC." + } + }, + { + "pk": 54, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead IAB Document", + "next_states": [], + "slug": "dead", + "type": "draft-stream-iab", + "order": 10, + "desc": "This document was an active IAB document, but for some reason it is no longer being pursued for the IAB stream. It is possible that the document might be revived later, possibly in another stream." + } + }, + { + "pk": 35, + "model": "doc.state", + "fields": { + "used": true, + "name": "Call For Adoption By WG Issued", + "next_states": [ + 36, + 37 + ], + "slug": "c-adopt", + "type": "draft-stream-ietf", + "order": 1, + "desc": "4.2.1. Call for Adoption by WG Issued\n\n The \"Call for Adoption by WG Issued\" state should be used to indicate when an I-D is being considered for adoption by an IETF WG. An I-D that is in this state is actively being considered for adoption and has not yet achieved consensus, preference, or selection in the WG.\n\n This state may be used to describe an I-D that someone has asked a WG to consider for adoption, if the WG Chair has agreed with the request. This state may also be used to identify an I-D that a WG Chair asked an author to write specifically for consideration as a candidate WG item [WGDTSPEC], and/or an I-D that is listed as a 'candidate draft' in the WG's charter.\n\n Under normal conditions, it should not be possible for an I-D to be in the \"Call for Adoption by WG Issued\" state in more than one working group at the same time. This said, it is not uncommon for authors to \"shop\" their I-Ds to more than one WG at a time, with the hope of getting their documents adopted somewhere.\n\n After this state is implemented in the Datatracker, an I-D that is in the \"Call for Adoption by WG Issued\" state will not be able to be \"shopped\" to any other WG without the consent of the WG Chairs and the responsible ADs impacted by the shopping.\n\n Note that Figure 1 includes an arc leading from this state to outside of the WG state machine. This illustrates that some I-Ds that are considered do not get adopted as WG drafts. An I-D that is not adopted as a WG draft will transition out of the WG state machine and revert back to having no stream-specific state; however, the status change history log of the I-D will record that the I-D was previously in the \"Call for Adoption by WG Issued\" state." + } + }, + { + "pk": 36, + "model": "doc.state", + "fields": { + "used": true, + "name": "Adopted by a WG", + "next_states": [ + 38 + ], + "slug": "adopt-wg", + "type": "draft-stream-ietf", + "order": 2, + "desc": "4.2.2. Adopted by a WG\n\n The \"Adopted by a WG\" state describes an individual submission I-D that an IETF WG has agreed to adopt as one of its WG drafts.\n\n WG Chairs who use this state will be able to clearly indicate when their WGs adopt individual submission I-Ds. This will facilitate the Datatracker's ability to correctly capture \"Replaces\" information for WG drafts and correct \"Replaced by\" information for individual submission I-Ds that have been replaced by WG drafts.\n\n This state is needed because the Datatracker uses the filename of an I-D as a key to search its database for status information about the I-D, and because the filename of a WG I-D is supposed to be different from the filename of an individual submission I-D. The filename of an individual submission I-D will typically be formatted as 'draft-author-wgname-topic-nn'.\n\n The filename of a WG document is supposed to be formatted as 'draft- ietf-wgname-topic-nn'.\n\n An individual I-D that is adopted by a WG may take weeks or months to be resubmitted by the author as a new (version-00) WG draft. If the \"Adopted by a WG\" state is not used, the Datatracker has no way to determine that an I-D has been adopted until a new version of the I-D is submitted to the WG by the author and until the I-D is approved for posting by a WG Chair." + } + }, + { + "pk": 37, + "model": "doc.state", + "fields": { + "used": true, + "name": "Adopted for WG Info Only", + "next_states": [], + "slug": "info", + "type": "draft-stream-ietf", + "order": 3, + "desc": "4.2.3. Adopted for WG Info Only\n\n The \"Adopted for WG Info Only\" state describes a document that contains useful information for the WG that adopted it, but the document is not intended to be published as an RFC. The WG will not actively develop the contents of the I-D or progress it for publication as an RFC. The only purpose of the I-D is to provide information for internal use by the WG." + } + }, + { + "pk": 38, + "model": "doc.state", + "fields": { + "used": true, + "name": "WG Document", + "next_states": [ + 39, + 40, + 41, + 43 + ], + "slug": "wg-doc", + "type": "draft-stream-ietf", + "order": 4, + "desc": "4.2.4. WG Document\n\n The \"WG Document\" state describes an I-D that has been adopted by an IETF WG and is being actively developed.\n\n A WG Chair may transition an I-D into the \"WG Document\" state at any time as long as the I-D is not being considered or developed in any other WG.\n\n Alternatively, WG Chairs may rely upon new functionality to be added to the Datatracker to automatically move version-00 drafts into the \"WG Document\" state as described in Section 4.1.\n\n Under normal conditions, it should not be possible for an I-D to be in the \"WG Document\" state in more than one WG at a time. This said, I-Ds may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs." + } + }, + { + "pk": 39, + "model": "doc.state", + "fields": { + "used": true, + "name": "Parked WG Document", + "next_states": [ + 38 + ], + "slug": "parked", + "type": "draft-stream-ietf", + "order": 5, + "desc": "4.2.5. Parked WG Document\n\n A \"Parked WG Document\" is an I-D that has lost its author or editor, is waiting for another document to be written or for a review to be completed, or cannot be progressed by the working group for some other reason.\n\n Some of the annotation tags described in Section 4.3 may be used in conjunction with this state to indicate why an I-D has been parked, and/or what may need to happen for the I-D to be un-parked.\n\n Parking a WG draft will not prevent it from expiring; however, this state can be used to indicate why the I-D has stopped progressing in the WG.\n\n A \"Parked WG Document\" that is not expired may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs." + } + }, + { + "pk": 40, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead WG Document", + "next_states": [ + 38 + ], + "slug": "dead", + "type": "draft-stream-ietf", + "order": 6, + "desc": "4.2.6. Dead WG Document\n\n A \"Dead WG Document\" is an I-D that has been abandoned. Note that 'Dead' is not always a final state for a WG I-D. If consensus is subsequently achieved, a \"Dead WG Document\" may be resurrected. A \"Dead WG Document\" that is not resurrected will eventually expire.\n\n Note that an I-D that is declared to be \"Dead\" in one WG and that is not expired may be transferred to a non-dead state in another WG with the consent of the WG Chairs and the responsible ADs." + } + }, + { + "pk": 41, + "model": "doc.state", + "fields": { + "used": true, + "name": "In WG Last Call", + "next_states": [ + 38, + 42, + 43 + ], + "slug": "wg-lc", + "type": "draft-stream-ietf", + "order": 7, + "desc": "4.2.7. In WG Last Call\n\n A document \"In WG Last Call\" is an I-D for which a WG Last Call (WGLC) has been issued and is in progress.\n\n Note that conducting a WGLC is an optional part of the IETF WG process, per Section 7.4 of RFC 2418 [RFC2418].\n\n If a WG Chair decides to conduct a WGLC on an I-D, the \"In WG Last Call\" state can be used to track the progress of the WGLC. The Chair may configure the Datatracker to send a WGLC message to one or more mailing lists when the Chair moves the I-D into this state. The WG Chair may also be able to select a different set of mailing lists for a different document undergoing a WGLC; some documents may deserve coordination with other WGs.\n\n A WG I-D in this state should remain \"In WG Last Call\" until the WG Chair moves it to another state. The WG Chair may configure the Datatracker to send an e-mail after a specified period of time to remind or 'nudge' the Chair to conclude the WGLC and to determine the next state for the document.\n\n It is possible for one WGLC to lead into another WGLC for the same document. For example, an I-D that completed a WGLC as an \"Informational\" document may need another WGLC if a decision is taken to convert the I-D into a Standards Track document." + } + }, + { + "pk": 42, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for WG Chair Go-Ahead", + "next_states": [ + 41, + 43 + ], + "slug": "chair-w", + "type": "draft-stream-ietf", + "order": 8, + "desc": "4.2.8. Waiting for WG Chair Go-Ahead\n\n A WG Chair may wish to place an I-D that receives a lot of comments during a WGLC into the \"Waiting for WG Chair Go-Ahead\" state. This state describes an I-D that has undergone a WGLC; however, the Chair is not yet ready to call consensus on the document.\n\n If comments from the WGLC need to be responded to, or a revision to the I-D is needed, the Chair may place an I-D into this state until all of the WGLC comments are adequately addressed and the (possibly revised) document is in the I-D repository." + } + }, + { + "pk": 43, + "model": "doc.state", + "fields": { + "used": true, + "name": "WG Consensus: Waiting for Write-Up", + "next_states": [ + 44 + ], + "slug": "writeupw", + "type": "draft-stream-ietf", + "order": 9, + "desc": "4.2.9. WG Consensus: Waiting for Writeup\n\n A document in the \"WG Consensus: Waiting for Writeup\" state has essentially completed its development within the working group, and is nearly ready to be sent to the IESG for publication. The last thing to be done is the preparation of a protocol writeup by a Document Shepherd. The IESG requires that a document shepherd writeup be completed before publication of the I-D is requested. The IETF document shepherding process and the role of a WG Document Shepherd is described in RFC 4858 [RFC4858]\n\n A WG Chair may call consensus on an I-D without a formal WGLC and transition an I-D that was in the \"WG Document\" state directly into this state.\n\n The name of this state includes the words \"Waiting for Writeup\" because a good document shepherd writeup takes time to prepare." + } + }, + { + "pk": 44, + "model": "doc.state", + "fields": { + "used": true, + "name": "Submitted to IESG for Publication", + "next_states": [ + 38 + ], + "slug": "sub-pub", + "type": "draft-stream-ietf", + "order": 10, + "desc": "4.2.10. Submitted to IESG for Publication\n\n This state describes a WG document that has been submitted to the IESG for publication and that has not been sent back to the working group for revision.\n\n An I-D in this state may be under review by the IESG, it may have been approved and be in the RFC Editor's queue, or it may have been published as an RFC. Other possibilities exist too. The document may be \"Dead\" (in the IESG state machine) or in a \"Do Not Publish\" state." + } + }, + { + "pk": 55, + "model": "doc.state", + "fields": { + "used": true, + "name": "Candidate RG Document", + "next_states": [], + "slug": "candidat", + "type": "draft-stream-irtf", + "order": 1, + "desc": "This document is under consideration in an RG for becoming an IRTF document. A document in this state does not imply any RG consensus and does not imply any precedence or selection. It's simply a way to indicate that somebody has asked for a document to be considered for adoption by an RG." + } + }, + { + "pk": 56, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active RG Document", + "next_states": [], + "slug": "active", + "type": "draft-stream-irtf", + "order": 2, + "desc": "This document has been adopted by the RG and is being actively developed." + } + }, + { + "pk": 57, + "model": "doc.state", + "fields": { + "used": true, + "name": "Parked RG Document", + "next_states": [], + "slug": "parked", + "type": "draft-stream-irtf", + "order": 3, + "desc": "This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the RG for some other reason." + } + }, + { + "pk": 58, + "model": "doc.state", + "fields": { + "used": true, + "name": "In RG Last Call", + "next_states": [], + "slug": "rg-lc", + "type": "draft-stream-irtf", + "order": 4, + "desc": "The document is in its final review in the RG." + } + }, + { + "pk": 59, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for Document Shepherd", + "next_states": [], + "slug": "sheph-w", + "type": "draft-stream-irtf", + "order": 5, + "desc": "IRTF documents have document shepherds who help RG documents through the process after the RG has finished with the document." + } + }, + { + "pk": 60, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for IRTF Chair", + "next_states": [], + "slug": "chair-w", + "type": "draft-stream-irtf", + "order": 6, + "desc": "The IRTF Chair is meant to be performing some task such as sending a request for IESG Review." + } + }, + { + "pk": 61, + "model": "doc.state", + "fields": { + "used": true, + "name": "Awaiting IRSG Reviews", + "next_states": [], + "slug": "irsg-w", + "type": "draft-stream-irtf", + "order": 7, + "desc": "The document shepherd has taken the document to the IRSG and solicited reviews from one or more IRSG members." + } + }, + { + "pk": 62, + "model": "doc.state", + "fields": { + "used": true, + "name": "In IRSG Poll", + "next_states": [], + "slug": "irsgpoll", + "type": "draft-stream-irtf", + "order": 8, + "desc": "The IRSG is taking a poll on whether or not the document is ready to be published." + } + }, + { + "pk": 63, + "model": "doc.state", + "fields": { + "used": true, + "name": "In IESG Review", + "next_states": [], + "slug": "iesg-rev", + "type": "draft-stream-irtf", + "order": 9, + "desc": "The IRSG has asked the IESG to do a review of the document, as described in RFC5742." + } + }, + { + "pk": 64, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to the RFC Editor", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-stream-irtf", + "order": 10, + "desc": "The RG processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the RG." + } + }, + { + "pk": 65, + "model": "doc.state", + "fields": { + "used": true, + "name": "Published RFC", + "next_states": [], + "slug": "pub", + "type": "draft-stream-irtf", + "order": 11, + "desc": "The document has been published as an RFC." + } + }, + { + "pk": 66, + "model": "doc.state", + "fields": { + "used": true, + "name": "Document on Hold Based On IESG Request", + "next_states": [], + "slug": "iesghold", + "type": "draft-stream-irtf", + "order": 12, + "desc": "The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the IRTF has agreed to such a hold." + } + }, + { + "pk": 67, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead IRTF Document", + "next_states": [], + "slug": "dead", + "type": "draft-stream-irtf", + "order": 13, + "desc": "This document was an active IRTF document, but for some reason it is no longer being pursued for the IRTF stream. It is possible that the document might be revived later, possibly in another stream." + } + }, + { + "pk": 68, + "model": "doc.state", + "fields": { + "used": true, + "name": "Submission Received", + "next_states": [], + "slug": "receive", + "type": "draft-stream-ise", + "order": 1, + "desc": "The draft has been sent to the ISE with a request for publication." + } + }, + { + "pk": 69, + "model": "doc.state", + "fields": { + "used": true, + "name": "Finding Reviewers", + "next_states": [], + "slug": "find-rev", + "type": "draft-stream-ise", + "order": 2, + "desc": " The ISE is finding initial reviewers for the document." + } + }, + { + "pk": 70, + "model": "doc.state", + "fields": { + "used": true, + "name": "In ISE Review", + "next_states": [], + "slug": "ise-rev", + "type": "draft-stream-ise", + "order": 3, + "desc": "The ISE is actively working on the document." + } + }, + { + "pk": 71, + "model": "doc.state", + "fields": { + "used": true, + "name": "Response to Review Needed", + "next_states": [], + "slug": "need-res", + "type": "draft-stream-ise", + "order": 4, + "desc": " One or more reviews have been sent to the author, and the ISE is awaiting response." + } + }, + { + "pk": 72, + "model": "doc.state", + "fields": { + "used": true, + "name": "In IESG Review", + "next_states": [], + "slug": "iesg-rev", + "type": "draft-stream-ise", + "order": 5, + "desc": "The ISE has asked the IESG to do a review of the document, as described in RFC5742." + } + }, + { + "pk": 73, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to the RFC Editor", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-stream-ise", + "order": 6, + "desc": "The ISE processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the ISE." + } + }, + { + "pk": 74, + "model": "doc.state", + "fields": { + "used": true, + "name": "Published RFC", + "next_states": [], + "slug": "pub", + "type": "draft-stream-ise", + "order": 7, + "desc": "The document has been published as an RFC." + } + }, + { + "pk": 75, + "model": "doc.state", + "fields": { + "used": true, + "name": "No Longer In Independent Submission Stream", + "next_states": [], + "slug": "dead", + "type": "draft-stream-ise", + "order": 8, + "desc": "This document was actively considered in the Independent Submission stream, but the ISE chose not to publish it. It is possible that the document might be revived later. A document in this state may have a comment explaining the reasoning of the ISE (such as if the document was going to move to a different stream)." + } + }, + { + "pk": 76, + "model": "doc.state", + "fields": { + "used": true, + "name": "Document on Hold Based On IESG Request", + "next_states": [], + "slug": "iesghold", + "type": "draft-stream-ise", + "order": 9, + "desc": "The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the ISE has agreed to such a hold." + } + }, + { + "pk": 79, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "minutes", + "order": 1, + "desc": "" + } + }, + { + "pk": 80, + "model": "doc.state", + "fields": { + "used": true, + "name": "Deleted", + "next_states": [], + "slug": "deleted", + "type": "minutes", + "order": 2, + "desc": "" + } + }, + { + "pk": 77, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "slides", + "order": 1, + "desc": "" + } + }, + { + "pk": 78, + "model": "doc.state", + "fields": { + "used": true, + "name": "Deleted", + "next_states": [], + "slug": "deleted", + "type": "slides", + "order": 2, + "desc": "" + } + }, + { + "pk": 121, + "model": "doc.state", + "fields": { + "used": true, + "name": "Needs Shepherd", + "next_states": [ + 122, + 129 + ], + "slug": "needshep", + "type": "statchg", + "order": 1, + "desc": "An RFC status change has been requested, but a shepherding AD has not yet been assigned" + } + }, + { + "pk": 122, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD Review", + "next_states": [ + 130, + 123, + 129 + ], + "slug": "adrev", + "type": "statchg", + "order": 2, + "desc": "The sponsoring AD is preparing an RFC status change document" + } + }, + { + "pk": 130, + "model": "doc.state", + "fields": { + "used": true, + "name": "Last Call Requested", + "next_states": [ + 131 + ], + "slug": "lc-req", + "type": "statchg", + "order": 3, + "desc": "Last Call has been requested for this proposed status change" + } + }, + { + "pk": 131, + "model": "doc.state", + "fields": { + "used": true, + "name": "In Last Call", + "next_states": [ + 132 + ], + "slug": "in-lc", + "type": "statchg", + "order": 4, + "desc": "This proposed status change is in IETF Last Call" + } + }, + { + "pk": 132, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for AD Go-Ahead", + "next_states": [ + 123, + 129 + ], + "slug": "goahead", + "type": "statchg", + "order": 5, + "desc": "The AD is following up on IETF LC comments" + } + }, + { + "pk": 123, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation", + "next_states": [ + 124, + 125, + 126, + 129 + ], + "slug": "iesgeval", + "type": "statchg", + "order": 6, + "desc": "The IESG is considering the proposed RFC status changes" + } + }, + { + "pk": 124, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation - Defer", + "next_states": [ + 123, + 125, + 126, + 129 + ], + "slug": "defer", + "type": "statchg", + "order": 7, + "desc": "The evaluation of the proposed RFC status changes have been deferred to the next telechat" + } + }, + { + "pk": 125, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved - point raised", + "next_states": [ + 126, + 127 + ], + "slug": "appr-pr", + "type": "statchg", + "order": 8, + "desc": "The IESG has approved the RFC status changes, but a point has been raised that should be cleared before proceeding to announcement to be sent" + } + }, + { + "pk": 126, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved - announcement to be sent", + "next_states": [ + 127 + ], + "slug": "appr-pend", + "type": "statchg", + "order": 9, + "desc": "The IESG has approved the RFC status changes, but the secretariat has not yet sent the announcement" + } + }, + { + "pk": 127, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved - announcement sent", + "next_states": [], + "slug": "appr-sent", + "type": "statchg", + "order": 10, + "desc": "The secretariat has announced the IESG's approved RFC status changes" + } + }, + { + "pk": 129, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead", + "next_states": [ + 121 + ], + "slug": "dead", + "type": "statchg", + "order": 11, + "desc": "The RFC status changes have been abandoned" + } + }, + { + "pk": 5, + "model": "doc.ballottype", + "fields": { + "doc_type": "conflrev", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "discuss", + "abstain", + "recuse", + "norecord" + ], + "question": "Is this the correct conflict review response?", + "slug": "conflrev", + "order": 0 + } + }, + { + "pk": 6, + "model": "doc.ballottype", + "fields": { + "doc_type": "statchg", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "discuss", + "abstain", + "recuse", + "norecord" + ], + "question": "Do we approve these RFC status changes?", + "slug": "statchg", + "order": 0 + } + }, + { + "pk": 1, + "model": "doc.ballottype", + "fields": { + "doc_type": "charter", + "used": true, + "name": "Ready for external review", + "positions": [ + "yes", + "noobj", + "block", + "abstain", + "norecord" + ], + "question": "Is this charter ready for external review?", + "slug": "r-extrev", + "order": 1 + } + }, + { + "pk": 4, + "model": "doc.ballottype", + "fields": { + "doc_type": "draft", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "discuss", + "abstain", + "recuse", + "norecord" + ], + "question": "", + "slug": "approve", + "order": 1 + } + }, + { + "pk": 2, + "model": "doc.ballottype", + "fields": { + "doc_type": "charter", + "used": true, + "name": "Ready w/o external review", + "positions": [ + "yes", + "noobj", + "block", + "abstain", + "norecord" + ], + "question": "Is this charter ready for external review? Is this charter ready for approval without external review?", + "slug": "r-wo-ext", + "order": 2 + } + }, + { + "pk": 3, + "model": "doc.ballottype", + "fields": { + "doc_type": "charter", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "block", + "abstain", + "norecord" + ], + "question": "Do we approve of this charter?", + "slug": "approve", + "order": 3 + } + } +] \ No newline at end of file diff --git a/ietf/name/fixtures/names.xml b/ietf/name/fixtures/names.xml deleted file mode 100644 index d8cca3fc8..000000000 --- a/ietf/name/fixtures/names.xml +++ /dev/null @@ -1,2274 +0,0 @@ - - - - Yes - - True - 1 - False - - - No Objection - - True - 2 - False - - - Discuss - - True - 3 - True - - - Block - - True - 3 - True - - - Abstain - - True - 4 - False - - - Recuse - - True - 5 - False - - - No Record - - True - 6 - False - - - Conflicts with - - True - 0 - - - Conflicts with (secondary) - - True - 0 - - - Conflicts with (tertiary) - - True - 0 - - - Obsoletes - - True - 0 - Obsoleted by - - - Updates - - True - 0 - Updated by - - - Replaces - - True - 0 - Replaced by - - - conflict reviews - - True - 0 - Conflict reviewed by - - - Normative Reference - Normative Reference - True - 0 - fixme - - - Reference - A reference found in a document which does not have split normative/informative reference sections. - True - 0 - fixme - - - Informative Reference - Informative Reference - True - 0 - fixme - - - Moves to Proposed Standard - - True - 0 - Moved to Proposed Standard by - - - Moves to Internet Standard - - True - 0 - Moved to Internet Standard by - - - Moves to Historic - - True - 0 - Moved to Historic by - - - Moves to Informational - - True - 0 - Moved to Informational by - - - Moves to BCP - - True - 0 - Moved to BCP by - - - Moves to Experimental - - True - 0 - Moved to Experimental by - - - Possible Reference - Reference of unknown type, likely found in the text of the document. - True - 3 - fixme - - - Stream state should change - - True - 0 - - - IANA coordination - RFC-Editor/IANA Registration Coordination - True - 0 - - - Holding for references - Holding for normative reference - True - 0 - - - Missing references - Awaiting missing normative reference - True - 0 - - - Has errata - - True - 0 - - - Review by RFC Editor - - True - 0 - - - Via RFC Editor - - True - 0 - - - Approved in minutes - - True - 0 - - - Shepherd Needed - - True - 0 - - - Waiting for Dependency on Other Document - - True - 0 - - - IESG Review Completed - - True - 0 - - - IANA - The document has IANA actions that are not yet completed. - True - 0 - - - Revised I-D Needed - Issue raised by WG - - True - 0 - - - Point Raised - writeup needed - IESG discussions on the document have raised some issues that need to be brought to the attention of the authors/WG, but those issues have not been written down yet. (It is common for discussions during a telechat to result in such situations. An AD may raise a possible issue during a telechat and only decide as a result of that discussion whether the issue is worth formally writing up and bringing to the attention of the authors/WG). A document stays in the "Point Raised - Writeup Needed" state until *ALL* IESG comments that have been raised have been documented. - True - 1 - - - Awaiting Expert Review/Resolution of Issues Raised - - True - 1 - - - Editor Needed - - True - 1 - - - AD Followup - A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include: - -- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG. - -- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently. - -- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes. - True - 2 - - - Awaiting External Review/Resolution of Issues Raised - - True - 2 - - - Waiting for Partner Feedback - - True - 2 - - - External Party - The document is awaiting review or input from an external party (i.e, someone other than the shepherding AD, the authors, or the WG). See the "note" field for more details on who has the action. - True - 3 - - - Awaiting Merge with Other Document - - True - 3 - - - Awaiting Reviews - - True - 3 - - - Author or Editor Needed - - True - 4 - - - Document Shepherd Followup - - True - 4 - - - Revised I-D Needed - An updated I-D is needed to address the issues that have been raised. - True - 5 - - - Waiting for Referenced Document - - True - 5 - - - Waiting for Referencing Document - - True - 6 - - - Revised I-D Needed - Issue raised by WGLC - - True - 7 - - - Revised I-D Needed - Issue raised by AD - - True - 8 - - - Revised I-D Needed - Issue raised by IESG - - True - 9 - - - Doc Shepherd Follow-up Underway - - True - 10 - - - Other - see Comment Log - - True - 11 - - - Charter - - True - 0 - - - Agenda - - True - 0 - - - Minutes - - True - 0 - - - Slides - - True - 0 - - - Draft - - True - 0 - - - Liaison Attachment - - True - 0 - - - Conflict Review - - True - 0 - - - Status Change - - True - 0 - - - Active - - True - 1 - - - Deleted - - True - 2 - - - For review - - True - 3 - - - Chartering/rechartering - - True - 4 - - - BOF - - True - 0 - - - Proposed - - True - 0 - - - Active - - True - 0 - - - Dormant - - True - 0 - - - Concluded - - True - 0 - - - Unknown - - True - 0 - - - Abandonded - Formation of the group (most likely a BoF or Proposed WG) was abandoned - True - 0 - - - BOF Concluded - - True - 0 - - - IETF - - True - 0 - - - Area - - True - 0 - - - AG - Area group - True - 0 - - - WG - Working group - True - 0 - - - RG - Research group - True - 0 - - - Team - - True - 0 - - - Individual - - True - 0 - - - SDO - Standards organization - True - 0 - - - IRTF - - True - 0 - - - RFC Editor - - True - 0 - - - NomCom - - True - 0 - - - Proposed Standard - - True - 1 - - - Draft Standard - - False - 2 - - - Internet Standard - - True - 3 - - - Best Current Practice - - True - 4 - - - Informational - - True - 5 - - - Experimental - - True - 6 - - - Historic - - True - 7 - - - For action - - True - 1 - - - For comment - - True - 2 - - - For information - - True - 3 - - - In response - - True - 4 - - - Pending - - True - 0 - - - Accepted - - True - 0 - - - Declined - - True - 0 - - - Comment - - True - 0 - - - Questionnaire response - - True - 0 - - - Nomination - - True - 0 - - - Offtopic - - True - 0 - - - reStructuredText - - True - 0 - - - Plain - - True - 0 - - - Django - - True - 0 - - - IETF - - True - 0 - - - Interim - - True - 0 - - - Area Director - - True - 0 - - - Incoming Area Director - - True - 0 - - - Chair - - True - 0 - - - Editor - - True - 0 - - - Secretary - - True - 0 - - - Tech Advisor - - True - 0 - - - Executive Director - - True - 0 - - - Administrative Director - - True - 0 - - - Liaison Manager - - True - 0 - - - Authorized Individual - - True - 0 - - - Delegate - - True - 0 - - - At Large Member - - True - 0 - - - Member - - True - 0 - - - Waiting for Scheduling - - True - 0 - - - Waiting for Approval - - True - 0 - - - Approved - - True - 0 - - - Scheduled - - True - 0 - - - Canceled - - True - 0 - - - Disapproved - - True - 0 - - - Not meeting - - True - 0 - - - Deleted - - True - 0 - - - Internet Standard - - True - 0 - - - Draft Standard - - False - 0 - - - Proposed Standard - - True - 0 - - - Informational - - True - 0 - - - Experimental - - True - 0 - - - Best Current Practice - - True - 0 - - - Historic - - True - 0 - - - Unknown - - True - 0 - - - IETF - IETF stream - True - 1 - - - ISE - Independent Submission Editor stream - True - 2 - - - IRTF - Independent Submission Editor stream - True - 3 - - - IAB - IAB stream - True - 4 - - - Legacy - Legacy stream - True - 5 - - - Other - - True - 0 - - - Session - - True - 0 - - - Break - - True - 0 - - - Registration - - True - 0 - - - Plenary - - True - 0 - - - State - - - IESG state - - - IANA state - - - RFC Editor state - - - IETF WG state - - - IRTF state - - - ISE state - - - IAB state - - - State - - - State - - - State - - - State - - - State - - - Conflict Review State - - - IANA Action state - - - IANA Review state - - - RFC Status Change - - - agenda - active - Active - True - - 1 - - - - agenda - deleted - Deleted - True - - 2 - - - - charter - notrev - Not currently under review - True - The proposed charter is not being considered at this time. A proposed charter will remain in this state until an AD moves it to Informal IESG review. - 0 - - - - charter - infrev - Informal IESG review - True - This is the initial state when an AD proposes a new charter. The normal next state is Internal review if the idea is accepted, or Not currently under review if the idea is abandoned. - 0 - - - - charter - intrev - Internal review - True - The IESG and IAB are reviewing the early draft of the charter; this is the initial IESG and IAB review. The usual next state is External review if the idea is adopted, or Informal IESG review if the IESG decides the idea needs more work, or Not currently under review is the idea is abandoned - 0 - - - - charter - extrev - External review - True - The IETF community and possibly other standards development organizations (SDOs) are reviewing the proposed charter. The usual next state is IESG review, although it might move to Not currently under review is the idea is abandoned during the external review. - 0 - - - - charter - iesgrev - IESG review - True - The IESG is reviewing the discussion from the external review of the proposed charter. The usual next state is Approved, or Not currently under review if the idea is abandoned. - 0 - - - - charter - approved - Approved - True - The charter is approved by the IESG. - 0 - - - - conflrev - needshep - Needs Shepherd - True - A conflict review has been requested, but a shepherding AD has not yet been assigned - 1 - - - - conflrev - adrev - AD Review - True - The sponsoring AD is reviewing the document and preparing a proposed response - 2 - - - - conflrev - iesgeval - IESG Evaluation - True - The IESG is considering the proposed conflict review response - 3 - - - - conflrev - defer - IESG Evaluation - Defer - True - The evaluation of the proposed conflict review response has been deferred to the next telechat - 4 - - - - conflrev - appr-reqnopub-pr - Approved Request to Not Publish - point raised - True - The IESG has approved the conflict review response (a request to not publish), but a point has been raised that should be cleared before moving to announcement to be sent - 5 - - - - conflrev - appr-noprob-pr - Approved No Problem - point raised - True - The IESG has approved the conflict review response, but a point has been raised that should be cleared before proceeding to announcement to be sent - 6 - - - - conflrev - appr-reqnopub-pend - Approved Request to Not Publish - announcement to be sent - True - The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response - 7 - - - - conflrev - appr-noprob-pend - Approved No Problem - announcement to be sent - True - The IESG has approved the conflict review response, but the secretariat has not yet sent the response - 8 - - - - conflrev - appr-reqnopub-sent - Approved Request to Not Publish - announcement sent - True - The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester - 9 - - - - conflrev - appr-noprob-sent - Approved No Problem - announcement sent - True - The secretariat has delivered the IESG's approved conflict review response to the requester - 10 - - - - conflrev - withdraw - Withdrawn - True - The request for conflict review was withdrawn - 11 - - - - conflrev - dead - Dead - True - The conflict review has been abandoned - 12 - - - - draft - active - Active - True - - 1 - - - - draft - expired - Expired - True - - 2 - - - - draft - rfc - RFC - True - - 3 - - - - draft - repl - Replaced - True - - 4 - - - - draft - auth-rm - Withdrawn by Submitter - True - - 5 - - - - draft - ietf-rm - Withdrawn by IETF - True - - 6 - - - - draft-iana-action - newdoc - New Document - True - A new document has been received by IANA, but no actions have been taken - 1 - - - - draft-iana-action - inprog - In Progress - True - IANA is currently processing the actions for this document - 2 - - - - draft-iana-action - waitauth - Waiting on Authors - True - IANA is waiting on the document's authors to respond - 3 - - - - draft-iana-action - waitad - Waiting on ADs - True - IANA is waiting on the IETF Area Directors to respond - 4 - - - - draft-iana-action - waitwgc - Waiting on WGC - True - IANA is waiting on the IETF Working Group Chairs to respond - 5 - - - - draft-iana-action - waitrfc - Waiting on RFC Editor - True - IANA has notified the RFC Editor that the actions have been completed - 6 - - - - draft-iana-action - rfcedack - RFC-Ed-Ack - True - Request completed. The RFC Editor has acknowledged receipt of IANA's message that the actions have been completed - 7 - - - - draft-iana-action - onhold - On Hold - True - IANA has suspended work on the document - 8 - - - - draft-iana-action - noic - No IC - True - Request completed. There were no IANA actions for this document - 9 - - - - draft-iana-review - need-rev - IANA - Review Needed - True - Document has not yet been reviewed by IANA. - 1 - - - - draft-iana-review - ok-act - IANA OK - Actions Needed - True - Document requires IANA actions, and the IANA Considerations section indicates the details of the actions correctly. - 2 - - - - draft-iana-review - ok-noact - IANA OK - No Actions Needed - True - Document requires no IANA action, and the IANA Considerations section indicates this correctly. - 3 - - - - draft-iana-review - not-ok - IANA - Not OK - True - IANA has issues with the text of the IANA Considerations section of the document. - 4 - - - - draft-iana-review - changed - Version Changed - Review Needed - True - Document revision has changed after review by IANA. - 5 - - - - draft-iesg - pub-req - Publication Requested - True - A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested. - 10 - - - - draft-iesg - ad-eval - AD Evaluation - True - A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole. - 11 - - - - draft-iesg - review-e - Expert Review - True - An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the "MIB doctors". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the "note" field for specific details on the nature of the review. - 12 - - - - draft-iesg - lc-req - Last Call Requested - True - The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet. - 15 - - - - draft-iesg - lc - In Last Call - True - The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks. - 16 - - - - draft-iesg - writeupw - Waiting for Writeup - True - Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC. - 18 - - - - draft-iesg - goaheadw - Waiting for AD Go-Ahead - True - As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole. - 19 - - - - draft-iesg - iesg-eva - IESG Evaluation - True - The document is now (finally!) being formally reviewed by the entire IESG. Documents are discussed in email or during a bi-weekly IESG telechat. In this phase, each AD reviews the document and airs any issues they may have. Unresolvable issues are documented as "discuss" comments that can be forwarded to the authors/WG. See the description of substates for additional details about the current state of the IESG discussion. - 20 - - - - draft-iesg - defer - IESG Evaluation - Defer - True - During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat. - 21 - - - - draft-iesg - approved - Approved-announcement to be sent - True - The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message. - 27 - - - - draft-iesg - ann - Approved-announcement sent - True - The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor. - 30 - - - - draft-iesg - rfcqueue - RFC Ed Queue - True - The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html). - 31 - - - - draft-iesg - pub - RFC Published - True - The ID has been published as an RFC. - 32 - - - - draft-iesg - nopubadw - DNP-waiting for AD note - True - Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the "note" field for more details on who has the action item. - 33 - - - - draft-iesg - nopubanw - DNP-announcement to be sent - True - The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official "do not publish" recommendation message. - 34 - - - - draft-iesg - watching - AD is watching - True - An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and "I-D Exists" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD's own reasons). - 42 - - - - draft-iesg - dead - Dead - True - Document is "dead" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.) - 99 - - - - draft-rfceditor - auth - AUTH - True - Awaiting author action - 0 - - - - draft-rfceditor - auth48 - AUTH48 - True - Awaiting final author approval - 0 - - - - draft-rfceditor - edit - EDIT - False - Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing - 0 - - - - draft-rfceditor - iana - IANA - True - Document has been edited, but is holding for completion of IANA actions - 0 - - - - draft-rfceditor - iesg - IESG - False - Awaiting IESG action - 0 - - - - draft-rfceditor - isr - ISR - True - Independent Submission Review by the ISE - 0 - - - - draft-rfceditor - isr-auth - ISR-AUTH - False - Independent submission awaiting author action, or in discussion between author and ISE - 0 - - - - draft-rfceditor - ref - REF - True - Holding for normative reference - 0 - - - - draft-rfceditor - rfc-edit - RFC-EDITOR - True - Awaiting final RFC Editor review before AUTH48 - 0 - - - - draft-rfceditor - timeout - TO - True - Time-out period during which the IESG reviews document for conflict/concurrence with other IETF working group work - 0 - - - - draft-rfceditor - missref - MISSREF - True - Awaiting missing normative reference - 0 - - - - draft-rfceditor - auth48done - AUTH48-DONE - False - Final approvals are complete - 0 - - - - draft-rfceditor - auth48-done - AUTH48-DONE - True - Final approvals are complete - 0 - - - - draft-rfceditor - edit - EDIT - True - Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing - 0 - - - - draft-rfceditor - iana-crd - IANA - True - RFC-Editor/IANA Registration Coordination - 0 - - - - draft-rfceditor - iesg - IESG - True - Holding for IESG action - 0 - - - - draft-rfceditor - isr-auth - ISR-AUTH - True - Independent Submission awaiting author update, or in discussion between author and ISE - 0 - - - - draft-stream-iab - candidat - Candidate IAB Document - True - A document being considered for the IAB stream. - 1 - - - - draft-stream-iab - active - Active IAB Document - True - This document has been adopted by the IAB and is being actively developed. - 2 - - - - draft-stream-iab - parked - Parked IAB Document - True - This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the IAB for some other reason. Annotations probably explain why this document is parked. - 3 - - - - draft-stream-iab - review-i - IAB Review - True - This document is awaiting the IAB itself to come to internal consensus. - 4 - - - - draft-stream-iab - review-c - Community Review - True - This document has completed internal consensus within the IAB and is now under community review. - 5 - - - - draft-stream-iab - approved - Approved by IAB, To Be Sent to RFC Editor - True - The consideration of this document is complete, but it has not yet been sent to the RFC Editor for publication (although that is going to happen soon). - 6 - - - - draft-stream-iab - diff-org - Sent to a Different Organization for Publication - True - The IAB does not expect to publish the document itself, but has passed it on to a different organization that might continue work on the document. The expectation is that the other organization will eventually publish the document. - 7 - - - - draft-stream-iab - rfc-edit - Sent to the RFC Editor - True - The IAB processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the IAB. - 8 - - - - draft-stream-iab - pub - Published RFC - True - The document has been published as an RFC. - 9 - - - - draft-stream-iab - dead - Dead IAB Document - True - This document was an active IAB document, but for some reason it is no longer being pursued for the IAB stream. It is possible that the document might be revived later, possibly in another stream. - 10 - - - - draft-stream-ietf - c-adopt - Call For Adoption By WG Issued - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.1" target="_blank">4.2.1. Call for Adoption by WG Issued</a> - - The "Call for Adoption by WG Issued" state should be used to indicate when an I-D is being considered for adoption by an IETF WG. An I-D that is in this state is actively being considered for adoption and has not yet achieved consensus, preference, or selection in the WG. - - This state may be used to describe an I-D that someone has asked a WG to consider for adoption, if the WG Chair has agreed with the request. This state may also be used to identify an I-D that a WG Chair asked an author to write specifically for consideration as a candidate WG item [WGDTSPEC], and/or an I-D that is listed as a 'candidate draft' in the WG's charter. - - Under normal conditions, it should not be possible for an I-D to be in the "Call for Adoption by WG Issued" state in more than one working group at the same time. This said, it is not uncommon for authors to "shop" their I-Ds to more than one WG at a time, with the hope of getting their documents adopted somewhere. - - After this state is implemented in the Datatracker, an I-D that is in the "Call for Adoption by WG Issued" state will not be able to be "shopped" to any other WG without the consent of the WG Chairs and the responsible ADs impacted by the shopping. - - Note that Figure 1 includes an arc leading from this state to outside of the WG state machine. This illustrates that some I-Ds that are considered do not get adopted as WG drafts. An I-D that is not adopted as a WG draft will transition out of the WG state machine and revert back to having no stream-specific state; however, the status change history log of the I-D will record that the I-D was previously in the "Call for Adoption by WG Issued" state. - 1 - - - - draft-stream-ietf - adopt-wg - Adopted by a WG - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.2" target="_blank">4.2.2. Adopted by a WG</a> - - The "Adopted by a WG" state describes an individual submission I-D that an IETF WG has agreed to adopt as one of its WG drafts. - - WG Chairs who use this state will be able to clearly indicate when their WGs adopt individual submission I-Ds. This will facilitate the Datatracker's ability to correctly capture "Replaces" information for WG drafts and correct "Replaced by" information for individual submission I-Ds that have been replaced by WG drafts. - - This state is needed because the Datatracker uses the filename of an I-D as a key to search its database for status information about the I-D, and because the filename of a WG I-D is supposed to be different from the filename of an individual submission I-D. The filename of an individual submission I-D will typically be formatted as 'draft-author-wgname-topic-nn'. - - The filename of a WG document is supposed to be formatted as 'draft- ietf-wgname-topic-nn'. - - An individual I-D that is adopted by a WG may take weeks or months to be resubmitted by the author as a new (version-00) WG draft. If the "Adopted by a WG" state is not used, the Datatracker has no way to determine that an I-D has been adopted until a new version of the I-D is submitted to the WG by the author and until the I-D is approved for posting by a WG Chair. - 2 - - - - draft-stream-ietf - info - Adopted for WG Info Only - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.3" target="_blank">4.2.3. Adopted for WG Info Only</a> - - The "Adopted for WG Info Only" state describes a document that contains useful information for the WG that adopted it, but the document is not intended to be published as an RFC. The WG will not actively develop the contents of the I-D or progress it for publication as an RFC. The only purpose of the I-D is to provide information for internal use by the WG. - 3 - - - - draft-stream-ietf - wg-doc - WG Document - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.4" target="_blank">4.2.4. WG Document</a> - - The "WG Document" state describes an I-D that has been adopted by an IETF WG and is being actively developed. - - A WG Chair may transition an I-D into the "WG Document" state at any time as long as the I-D is not being considered or developed in any other WG. - - Alternatively, WG Chairs may rely upon new functionality to be added to the Datatracker to automatically move version-00 drafts into the "WG Document" state as described in Section 4.1. - - Under normal conditions, it should not be possible for an I-D to be in the "WG Document" state in more than one WG at a time. This said, I-Ds may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs. - 4 - - - - draft-stream-ietf - parked - Parked WG Document - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.5" target="_blank">4.2.5. Parked WG Document</a> - - A "Parked WG Document" is an I-D that has lost its author or editor, is waiting for another document to be written or for a review to be completed, or cannot be progressed by the working group for some other reason. - - Some of the annotation tags described in Section 4.3 may be used in conjunction with this state to indicate why an I-D has been parked, and/or what may need to happen for the I-D to be un-parked. - - Parking a WG draft will not prevent it from expiring; however, this state can be used to indicate why the I-D has stopped progressing in the WG. - - A "Parked WG Document" that is not expired may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs. - 5 - - - - draft-stream-ietf - dead - Dead WG Document - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.6" target="_blank">4.2.6. Dead WG Document</a> - - A "Dead WG Document" is an I-D that has been abandoned. Note that 'Dead' is not always a final state for a WG I-D. If consensus is subsequently achieved, a "Dead WG Document" may be resurrected. A "Dead WG Document" that is not resurrected will eventually expire. - - Note that an I-D that is declared to be "Dead" in one WG and that is not expired may be transferred to a non-dead state in another WG with the consent of the WG Chairs and the responsible ADs. - 6 - - - - draft-stream-ietf - wg-lc - In WG Last Call - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.7" target="_blank">4.2.7. In WG Last Call</a> - - A document "In WG Last Call" is an I-D for which a WG Last Call (WGLC) has been issued and is in progress. - - Note that conducting a WGLC is an optional part of the IETF WG process, per Section 7.4 of RFC 2418 [RFC2418]. - - If a WG Chair decides to conduct a WGLC on an I-D, the "In WG Last Call" state can be used to track the progress of the WGLC. The Chair may configure the Datatracker to send a WGLC message to one or more mailing lists when the Chair moves the I-D into this state. The WG Chair may also be able to select a different set of mailing lists for a different document undergoing a WGLC; some documents may deserve coordination with other WGs. - - A WG I-D in this state should remain "In WG Last Call" until the WG Chair moves it to another state. The WG Chair may configure the Datatracker to send an e-mail after a specified period of time to remind or 'nudge' the Chair to conclude the WGLC and to determine the next state for the document. - - It is possible for one WGLC to lead into another WGLC for the same document. For example, an I-D that completed a WGLC as an "Informational" document may need another WGLC if a decision is taken to convert the I-D into a Standards Track document. - 7 - - - - draft-stream-ietf - chair-w - Waiting for WG Chair Go-Ahead - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.8" target="_blank">4.2.8. Waiting for WG Chair Go-Ahead</a> - - A WG Chair may wish to place an I-D that receives a lot of comments during a WGLC into the "Waiting for WG Chair Go-Ahead" state. This state describes an I-D that has undergone a WGLC; however, the Chair is not yet ready to call consensus on the document. - - If comments from the WGLC need to be responded to, or a revision to the I-D is needed, the Chair may place an I-D into this state until all of the WGLC comments are adequately addressed and the (possibly revised) document is in the I-D repository. - 8 - - - - draft-stream-ietf - writeupw - WG Consensus: Waiting for Write-Up - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.9" target="_blank">4.2.9. WG Consensus: Waiting for Writeup</a> - - A document in the "WG Consensus: Waiting for Writeup" state has essentially completed its development within the working group, and is nearly ready to be sent to the IESG for publication. The last thing to be done is the preparation of a protocol writeup by a Document Shepherd. The IESG requires that a document shepherd writeup be completed before publication of the I-D is requested. The IETF document shepherding process and the role of a WG Document Shepherd is described in RFC 4858 [RFC4858] - - A WG Chair may call consensus on an I-D without a formal WGLC and transition an I-D that was in the "WG Document" state directly into this state. - - The name of this state includes the words "Waiting for Writeup" because a good document shepherd writeup takes time to prepare. - 9 - - - - draft-stream-ietf - sub-pub - Submitted to IESG for Publication - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.10" target="_blank">4.2.10. Submitted to IESG for Publication</a> - - This state describes a WG document that has been submitted to the IESG for publication and that has not been sent back to the working group for revision. - - An I-D in this state may be under review by the IESG, it may have been approved and be in the RFC Editor's queue, or it may have been published as an RFC. Other possibilities exist too. The document may be "Dead" (in the IESG state machine) or in a "Do Not Publish" state. - 10 - - - - draft-stream-irtf - candidat - Candidate RG Document - True - This document is under consideration in an RG for becoming an IRTF document. A document in this state does not imply any RG consensus and does not imply any precedence or selection. It's simply a way to indicate that somebody has asked for a document to be considered for adoption by an RG. - 1 - - - - draft-stream-irtf - active - Active RG Document - True - This document has been adopted by the RG and is being actively developed. - 2 - - - - draft-stream-irtf - parked - Parked RG Document - True - This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the RG for some other reason. - 3 - - - - draft-stream-irtf - rg-lc - In RG Last Call - True - The document is in its final review in the RG. - 4 - - - - draft-stream-irtf - sheph-w - Waiting for Document Shepherd - True - IRTF documents have document shepherds who help RG documents through the process after the RG has finished with the document. - 5 - - - - draft-stream-irtf - chair-w - Waiting for IRTF Chair - True - The IRTF Chair is meant to be performing some task such as sending a request for IESG Review. - 6 - - - - draft-stream-irtf - irsg-w - Awaiting IRSG Reviews - True - The document shepherd has taken the document to the IRSG and solicited reviews from one or more IRSG members. - 7 - - - - draft-stream-irtf - irsgpoll - In IRSG Poll - True - The IRSG is taking a poll on whether or not the document is ready to be published. - 8 - - - - draft-stream-irtf - iesg-rev - In IESG Review - True - The IRSG has asked the IESG to do a review of the document, as described in RFC5742. - 9 - - - - draft-stream-irtf - rfc-edit - Sent to the RFC Editor - True - The RG processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the RG. - 10 - - - - draft-stream-irtf - pub - Published RFC - True - The document has been published as an RFC. - 11 - - - - draft-stream-irtf - iesghold - Document on Hold Based On IESG Request - True - The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the IRTF has agreed to such a hold. - 12 - - - - draft-stream-irtf - dead - Dead IRTF Document - True - This document was an active IRTF document, but for some reason it is no longer being pursued for the IRTF stream. It is possible that the document might be revived later, possibly in another stream. - 13 - - - - draft-stream-ise - receive - Submission Received - True - The draft has been sent to the ISE with a request for publication. - 1 - - - - draft-stream-ise - find-rev - Finding Reviewers - True - The ISE is finding initial reviewers for the document. - 2 - - - - draft-stream-ise - ise-rev - In ISE Review - True - The ISE is actively working on the document. - 3 - - - - draft-stream-ise - need-res - Response to Review Needed - True - One or more reviews have been sent to the author, and the ISE is awaiting response. - 4 - - - - draft-stream-ise - iesg-rev - In IESG Review - True - The ISE has asked the IESG to do a review of the document, as described in RFC5742. - 5 - - - - draft-stream-ise - rfc-edit - Sent to the RFC Editor - True - The ISE processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the ISE. - 6 - - - - draft-stream-ise - pub - Published RFC - True - The document has been published as an RFC. - 7 - - - - draft-stream-ise - dead - No Longer In Independent Submission Stream - True - This document was actively considered in the Independent Submission stream, but the ISE chose not to publish it. It is possible that the document might be revived later. A document in this state may have a comment explaining the reasoning of the ISE (such as if the document was going to move to a different stream). - 8 - - - - draft-stream-ise - iesghold - Document on Hold Based On IESG Request - True - The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the ISE has agreed to such a hold. - 9 - - - - minutes - active - Active - True - - 1 - - - - minutes - deleted - Deleted - True - - 2 - - - - slides - active - Active - True - - 1 - - - - slides - deleted - Deleted - True - - 2 - - - - statchg - needshep - Needs Shepherd - True - An RFC status change has been requested, but a shepherding AD has not yet been assigned - 1 - - - - statchg - adrev - AD Review - True - The sponsoring AD is preparing an RFC status change document - 2 - - - - statchg - lc-req - Last Call Requested - True - Last Call has been requested for this proposed status change - 3 - - - - statchg - in-lc - In Last Call - True - This proposed status change is in IETF Last Call - 4 - - - - statchg - goahead - Waiting for AD Go-Ahead - True - The AD is following up on IETF LC comments - 5 - - - - statchg - iesgeval - IESG Evaluation - True - The IESG is considering the proposed RFC status changes - 6 - - - - statchg - defer - IESG Evaluation - Defer - True - The evaluation of the proposed RFC status changes have been deferred to the next telechat - 7 - - - - statchg - appr-pr - Approved - point raised - True - The IESG has approved the RFC status changes, but a point has been raised that should be cleared before proceeding to announcement to be sent - 8 - - - - statchg - appr-pend - Approved - announcement to be sent - True - The IESG has approved the RFC status changes, but the secretariat has not yet sent the announcement - 9 - - - - statchg - appr-sent - Approved - announcement sent - True - The secretariat has announced the IESG's approved RFC status changes - 10 - - - - statchg - dead - Dead - True - The RFC status changes have been abandoned - 11 - - - - conflrev - conflrev - Approve - Is this the correct conflict review response? - True - 0 - - - - statchg - statchg - Approve - Do we approve these RFC status changes? - True - 0 - - - - charter - r-extrev - Ready for external review - Is this charter ready for external review? - True - 1 - - - - draft - approve - Approve - - True - 1 - - - - charter - r-wo-ext - Ready w/o external review - Is this charter ready for external review? Is this charter ready for approval without external review? - True - 2 - - - - charter - approve - Approve - Do we approve of this charter? - True - 3 - - - diff --git a/ietf/name/generate_fixtures.py b/ietf/name/generate_fixtures.py index c4b8bed35..58a485df3 100755 --- a/ietf/name/generate_fixtures.py +++ b/ietf/name/generate_fixtures.py @@ -17,8 +17,8 @@ from django.db.models import Q def output(name, qs): try: - f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.xml" % name), 'w') - f.write(serialize("xml", qs, indent=4)) + f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.json" % name), 'w') + f.write(serialize("json", qs, indent=1)) f.close() except: from django.db import connection From 0c0c0c91949682d072bd616a767dbffe7891db81 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sun, 29 Sep 2013 15:48:44 +0000 Subject: [PATCH 048/173] Fix test fixture generation, it wasn't catching new name models not ending in Name - Legacy-Id: 6303 --- ietf/name/fixtures/names.json | 121 +++++++++++++++++++++++++++++++++ ietf/name/generate_fixtures.py | 9 +-- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json index d87bfe154..adc0d3fbc 100644 --- a/ietf/name/fixtures/names.json +++ b/ietf/name/fixtures/names.json @@ -81,6 +81,7 @@ "model": "name.constraintname", "fields": { "order": 0, + "penalty": 0, "used": true, "name": "Conflicts with", "desc": "" @@ -91,6 +92,7 @@ "model": "name.constraintname", "fields": { "order": 0, + "penalty": 0, "used": true, "name": "Conflicts with (secondary)", "desc": "" @@ -101,6 +103,7 @@ "model": "name.constraintname", "fields": { "order": 0, + "penalty": 0, "used": true, "name": "Conflicts with (tertiary)", "desc": "" @@ -700,6 +703,56 @@ "desc": "" } }, + { + "pk": "liaison", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": false, + "name": "Liaison", + "desc": "" + } + }, + { + "pk": "comment", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Comment", + "desc": "" + } + }, + { + "pk": "questio", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Questionnaire response", + "desc": "" + } + }, + { + "pk": "nomina", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Nomination", + "desc": "" + } + }, + { + "pk": "junk", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Junk", + "desc": "" + } + }, { "pk": "active", "model": "name.groupmilestonestatename", @@ -920,6 +973,16 @@ "desc": "" } }, + { + "pk": "nomcom", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Nomcom", + "desc": "An IETF/IAB Nominating Committee. Use 'SDO' for external nominating committees." + } + }, { "pk": "ps", "model": "name.intendedstdlevelname", @@ -1050,6 +1113,36 @@ "desc": "" } }, + { + "pk": "pending", + "model": "name.nomineepositionstate", + "fields": { + "order": 0, + "used": true, + "name": "Nominated, pending response", + "desc": "" + } + }, + { + "pk": "accepted", + "model": "name.nomineepositionstate", + "fields": { + "order": 0, + "used": true, + "name": "Accepted", + "desc": "" + } + }, + { + "pk": "declined", + "model": "name.nomineepositionstate", + "fields": { + "order": 0, + "used": true, + "name": "Declined", + "desc": "" + } + }, { "pk": "ad", "model": "name.rolename", @@ -2623,6 +2716,19 @@ "desc": "Independent Submission awaiting author update, or in discussion between author and ISE" } }, + { + "pk": 133, + "model": "doc.state", + "fields": { + "used": true, + "name": "Pending", + "next_states": [], + "slug": "pending", + "type": "draft-rfceditor", + "order": 0, + "desc": "" + } + }, { "pk": 45, "model": "doc.state", @@ -2753,6 +2859,21 @@ "desc": "This document was an active IAB document, but for some reason it is no longer being pursued for the IAB stream. It is possible that the document might be revived later, possibly in another stream." } }, + { + "pk": 134, + "model": "doc.state", + "fields": { + "used": true, + "name": "Candidate for WG Adoption", + "next_states": [ + 35 + ], + "slug": "wg-cand", + "type": "draft-stream-ietf", + "order": 0, + "desc": "The document has been marked as a candidate for WG adoption by the WG Chair. This state can be used before a call for adoption is issued (and the document is put in the \"Call For Adoption By WG Issued\" state), to indicate that the document is in the queue for a call for adoption, even if none has been issued yet." + } + }, { "pk": 35, "model": "doc.state", diff --git a/ietf/name/generate_fixtures.py b/ietf/name/generate_fixtures.py index 58a485df3..144f17475 100755 --- a/ietf/name/generate_fixtures.py +++ b/ietf/name/generate_fixtures.py @@ -29,12 +29,13 @@ def output(name, qs): # pick all name models directly out of the module objects = [] +import inspect import ietf.name.models for n in dir(ietf.name.models): - if n[:1].upper() == n[:1] and n.endswith("Name"): - model = getattr(ietf.name.models, n) - if not model._meta.abstract: - objects.extend(model.objects.all()) + symbol = getattr(ietf.name.models, n) + if inspect.isclass(symbol) and issubclass(symbol, ietf.name.models.NameModel): + if not symbol._meta.abstract: + objects.extend(symbol.objects.all()) import ietf.doc.models # also pick some other name-like types while we're at it From f13837ef5fd77db64163cc84358c9640aa3ff9e9 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sun, 29 Sep 2013 17:32:52 +0000 Subject: [PATCH 049/173] Add support for global test fixtures to avoid loading them for all tests - Legacy-Id: 6304 --- ietf/settings.py | 3 ++- ietf/utils/test_runner.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ietf/settings.py b/ietf/settings.py index ee09083d9..3887097de 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -348,7 +348,8 @@ HTPASSWD_FILE = "/www/htpasswd" # DB redesign USE_DB_REDESIGN_PROXY_CLASSES = True -SOUTH_TESTS_MIGRATE = False +SOUTH_TESTS_MIGRATE = False +TEST_GLOBAL_FIXTURES = ["names"] # fixtures loaded for all tests by test runner # Generation of bibxml files for xml2rfc BIBXML_BASE_PATH = '/a/www/ietf-ftp/xml2rfc' diff --git a/ietf/utils/test_runner.py b/ietf/utils/test_runner.py index 064874473..2a5fb9fdf 100644 --- a/ietf/utils/test_runner.py +++ b/ietf/utils/test_runner.py @@ -37,6 +37,7 @@ import socket from django.conf import settings from django.template import TemplateDoesNotExist from django.test.simple import run_tests as django_run_tests +from django.core.management import call_command import debug @@ -55,6 +56,9 @@ def safe_create_1(self, verbosity, *args, **kwargs): x = old_create(self, 0, *args, **kwargs) print " Saving test database name "+settings.DATABASES["default"]["NAME"]+"..." test_database_name = settings.DATABASES["default"]["NAME"] + if settings.TEST_GLOBAL_FIXTURES: + print " Loading globale test fixtures: %s" % ", ".join(settings.TEST_GLOBAL_FIXTURES) + call_command('loaddata', *settings.TEST_GLOBAL_FIXTURES, verbosity=0, commit=False, database="default") return x def safe_destroy_0_1(*args, **kwargs): From c191a691e0abb0f7c415deb9bde3bf632cc0b5c0 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Sun, 29 Sep 2013 17:35:59 +0000 Subject: [PATCH 050/173] Remove "names" fixture from all tests, since it's now loaded once just after creating the test database through the global test fixture mechanism - this speeds up multiple tests considerably - Legacy-Id: 6305 --- ietf/announcements/tests.py | 2 - ietf/doc/tests.py | 6 - ietf/doc/tests_ballot.py | 20 +-- ietf/doc/tests_conflict_review.py | 10 +- ietf/doc/tests_draft.py | 37 +---- ietf/doc/tests_status_change.py | 10 +- ietf/group/tests.py | 2 - ietf/idindex/tests.py | 4 +- ietf/iesg/tests.py | 257 ------------------------------ ietf/liaisons/tests.py | 4 +- ietf/nomcom/tests.py | 6 +- ietf/secr/announcement/tests.py | 6 - ietf/secr/areas/tests.py | 2 - ietf/secr/drafts/tests.py | 4 +- ietf/secr/groups/tests.py | 1 - ietf/secr/ipradmin/tests.py | 4 +- ietf/secr/meetings/tests.py | 2 - ietf/secr/proceedings/tests.py | 2 - ietf/secr/roles/tests.py | 2 - ietf/secr/rolodex/tests.py | 2 - ietf/secr/sreq/tests.py | 6 +- ietf/secr/telechat/tests.py | 2 - ietf/submit/tests.py | 8 +- ietf/sync/tests.py | 16 +- ietf/wgcharter/tests.py | 8 +- ietf/wginfo/tests.py | 14 +- 26 files changed, 38 insertions(+), 399 deletions(-) diff --git a/ietf/announcements/tests.py b/ietf/announcements/tests.py index 92bf9daa9..b09ee29e4 100644 --- a/ietf/announcements/tests.py +++ b/ietf/announcements/tests.py @@ -67,8 +67,6 @@ class SendScheduledAnnouncementsTestCase(django.test.TestCase): class SendScheduledAnnouncementsTestCaseREDESIGN(django.test.TestCase): - fixtures = ["names"] - def test_send_plain_announcement(self): make_test_data() diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index b3cea4d96..c6aee0e2d 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -24,8 +24,6 @@ from ietf.doc.tests_status_change import * class SearchTestCase(django.test.TestCase): - fixtures = ['names'] - def test_search(self): draft = make_test_data() @@ -126,8 +124,6 @@ class SearchTestCase(django.test.TestCase): class DocTestCase(django.test.TestCase): - fixtures = ['names'] - def test_document_draft(self): draft = make_test_data() @@ -249,8 +245,6 @@ class DocTestCase(django.test.TestCase): class AddCommentTestCase(django.test.TestCase): - fixtures = ['names'] - def test_add_comment(self): draft = make_test_data() url = urlreverse('doc_add_comment', kwargs=dict(name=draft.name)) diff --git a/ietf/doc/tests_ballot.py b/ietf/doc/tests_ballot.py index 5db94a39b..82888483d 100644 --- a/ietf/doc/tests_ballot.py +++ b/ietf/doc/tests_ballot.py @@ -20,9 +20,7 @@ from ietf.utils.test_utils import login_testing_unauthorized from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox -class EditPositionTestCase(django.test.TestCase): - fixtures = ['names'] - +class EditPositionTests(django.test.TestCase): def test_edit_position(self): draft = make_test_data() url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name, @@ -169,9 +167,7 @@ class EditPositionTestCase(django.test.TestCase): self.assertTrue("Test!" in str(m)) -class DeferBallotTestCase(django.test.TestCase): - fixtures = ['names'] - +class DeferBallotTests(django.test.TestCase): def test_defer_ballot(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva")) @@ -215,9 +211,7 @@ class DeferBallotTestCase(django.test.TestCase): draft = Document.objects.get(name=draft.name) self.assertEquals(draft.get_state_slug("draft-iesg"), "iesg-eva") -class BallotWriteupsTestCase(django.test.TestCase): - fixtures = ['names'] - +class BallotWriteupsTests(django.test.TestCase): def test_edit_last_call_text(self): draft = make_test_data() url = urlreverse('doc_ballot_lastcall', kwargs=dict(name=draft.name)) @@ -406,9 +400,7 @@ class BallotWriteupsTestCase(django.test.TestCase): draft = Document.objects.get(name=draft.name) self.assertTrue("Subject: Results of IETF-conflict review" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text) -class ApproveBallotTestCase(django.test.TestCase): - fixtures = ['names'] - +class ApproveBallotTests(django.test.TestCase): def test_approve_ballot(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva")) # make sure it's approvable @@ -456,9 +448,7 @@ class ApproveBallotTestCase(django.test.TestCase): self.assertEquals(len(outbox), mailbox_before + 3) self.assertTrue("NOT be published" in str(outbox[-1])) -class MakeLastCallTestCase(django.test.TestCase): - fixtures = ['names'] - +class MakeLastCallTests(django.test.TestCase): def test_make_last_call(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="lc-req")) diff --git a/ietf/doc/tests_conflict_review.py b/ietf/doc/tests_conflict_review.py index d0ad46eb4..6ccc494ff 100644 --- a/ietf/doc/tests_conflict_review.py +++ b/ietf/doc/tests_conflict_review.py @@ -22,10 +22,7 @@ from ietf.group.models import Person from ietf.iesg.models import TelechatDate -class ConflictReviewTestCase(django.test.TestCase): - - fixtures = ['names'] - +class ConflictReviewTests(django.test.TestCase): def test_start_review(self): doc = Document.objects.get(name='draft-imaginary-independent-submission') @@ -254,10 +251,7 @@ class ConflictReviewTestCase(django.test.TestCase): make_test_data() -class ConflictReviewSubmitTestCase(django.test.TestCase): - - fixtures = ['names'] - +class ConflictReviewSubmitTests(django.test.TestCase): def test_initial_submission(self): doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission') url = urlreverse('conflict_review_submit',kwargs=dict(name=doc.name)) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 5d4c873ea..ea28809dd 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -22,9 +22,7 @@ from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox -class ChangeStateTestCase(django.test.TestCase): - fixtures = ['names'] - +class ChangeStateTests(django.test.TestCase): def test_change_state(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="ad-eval")) @@ -178,9 +176,7 @@ class ChangeStateTestCase(django.test.TestCase): self.assertTrue("Last call was requested" in draft.latest_event().desc) -class EditInfoTestCase(django.test.TestCase): - fixtures = ['names'] - +class EditInfoTests(django.test.TestCase): def test_edit_info(self): draft = make_test_data() url = urlreverse('doc_edit_info', kwargs=dict(name=draft.name)) @@ -360,9 +356,7 @@ class EditInfoTestCase(django.test.TestCase): self.assertEqual(draft.latest_event(ConsensusDocEvent, type="changed_consensus").consensus, True) -class ResurrectTestCase(django.test.TestCase): - fixtures = ['names'] - +class ResurrectTests(django.test.TestCase): def test_request_resurrect(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft", slug="expired")) @@ -427,9 +421,7 @@ class ResurrectTestCase(django.test.TestCase): self.assertEquals(len(outbox), mailbox_before + 1) -class ExpireIDsTestCase(django.test.TestCase): - fixtures = ['names'] - +class ExpireIDsTests(django.test.TestCase): def setUp(self): self.id_dir = os.path.abspath("tmp-id-dir") self.archive_dir = os.path.abspath("tmp-id-archive") @@ -609,9 +601,7 @@ class ExpireIDsTestCase(django.test.TestCase): self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt))) self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "deleted_tombstones", txt))) -class ExpireLastCallTestCase(django.test.TestCase): - fixtures = ['names'] - +class ExpireLastCallTests(django.test.TestCase): def test_expire_last_call(self): from ietf.doc.lastcall import get_expired_last_calls, expire_last_call @@ -658,10 +648,7 @@ class ExpireLastCallTestCase(django.test.TestCase): self.assertEquals(len(outbox), mailbox_before + 1) self.assertTrue("Last Call Expired" in outbox[-1]["Subject"]) -class IndividualInfoFormsTestCase(django.test.TestCase): - - fixtures = ['names'] - +class IndividualInfoFormsTests(django.test.TestCase): def test_doc_change_stream(self): url = urlreverse('doc_change_stream', kwargs=dict(name=self.docname)) login_testing_unauthorized(self, "secretary", url) @@ -886,9 +873,7 @@ class IndividualInfoFormsTestCase(django.test.TestCase): self.docname='draft-ietf-mars-test' self.doc = Document.objects.get(name=self.docname) -class SubmitToIesgTestCase(django.test.TestCase): - fixtures = ['names'] - +class SubmitToIesgTests(django.test.TestCase): def verify_permissions(self): def verify_fail(remote_user): @@ -946,9 +931,7 @@ class SubmitToIesgTestCase(django.test.TestCase): self.doc = Document.objects.get(name=self.docname) self.doc.unset_state('draft-iesg') -class RequestPublicationTestCase(django.test.TestCase): - fixtures = ['names'] - +class RequestPublicationTests(django.test.TestCase): def test_request_publication(self): draft = make_test_data() draft.stream = StreamName.objects.get(slug="iab") @@ -986,8 +969,6 @@ class RequestPublicationTestCase(django.test.TestCase): self.assertTrue(not outbox[-1]['CC']) class AdoptDraftTests(django.test.TestCase): - fixtures = ['names'] - def test_adopt_document(self): draft = make_test_data() draft.stream = None @@ -1023,8 +1004,6 @@ class AdoptDraftTests(django.test.TestCase): self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) class ChangeStreamStateTests(django.test.TestCase): - fixtures = ['names'] - def test_set_tags(self): draft = make_test_data() draft.tags = DocTagName.objects.filter(slug="w-expert") diff --git a/ietf/doc/tests_status_change.py b/ietf/doc/tests_status_change.py index bcd10d181..06bc11a46 100644 --- a/ietf/doc/tests_status_change.py +++ b/ietf/doc/tests_status_change.py @@ -22,10 +22,7 @@ from ietf.group.models import Person from ietf.iesg.models import TelechatDate -class StatusChangeTestCase(django.test.TestCase): - - fixtures = ['names'] - +class StatusChangeTests(django.test.TestCase): def test_start_review(self): url = urlreverse('start_rfc_status_change',kwargs=dict(name="")) @@ -353,10 +350,7 @@ class StatusChangeTestCase(django.test.TestCase): make_test_data() -class StatusChangeSubmitTestCase(django.test.TestCase): - - fixtures = ['names'] - +class StatusChangeSubmitTests(django.test.TestCase): def test_initial_submission(self): doc = Document.objects.get(name='status-change-imaginary-mid-review') url = urlreverse('status_change_submit',kwargs=dict(name=doc.name)) diff --git a/ietf/group/tests.py b/ietf/group/tests.py index db3e326e8..2dd04fbbb 100644 --- a/ietf/group/tests.py +++ b/ietf/group/tests.py @@ -14,8 +14,6 @@ from ietf.group.models import * from ietf.person.models import * class StreamTests(django.test.TestCase): - fixtures = ['names'] - def test_stream_edit(self): make_test_data() diff --git a/ietf/idindex/tests.py b/ietf/idindex/tests.py index 54e71ba17..2c25da365 100644 --- a/ietf/idindex/tests.py +++ b/ietf/idindex/tests.py @@ -9,9 +9,7 @@ from ietf.doc.models import * from ietf.idindex.index import * -class IndexTestCase(django.test.TestCase): - fixtures = ['names'] - +class IndexTests(django.test.TestCase): def setUp(self): self.id_dir = os.path.abspath("tmp-id-dir") os.mkdir(self.id_dir) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 3f1fbdf7a..a6302338e 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -12,50 +12,6 @@ from ietf.iesg.models import * from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized class RescheduleOnAgendaTestCase(django.test.TestCase): - fixtures = ['base', 'draft'] - - def test_reschedule(self): - draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6") - draft.idinternal.telechat_date = TelechatDates.objects.all()[0].dates()[0] - draft.idinternal.agenda = True - draft.idinternal.returning_item = True - draft.idinternal.save() - - form_id = draft.idinternal.draft_id - telechat_date_before = draft.idinternal.telechat_date - - url = urlreverse('ietf.iesg.views.agenda_documents') - self.client.login(remote_user="klm") - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form select[name=%s-telechat_date]' % form_id)), 1) - self.assertEquals(len(q('form input[name=%s-clear_returning_item]' % form_id)), 1) - - # reschedule - comments_before = draft.idinternal.comments().count() - d = TelechatDates.objects.all()[0].dates()[2] - - r = self.client.post(url, { '%s-telechat_date' % form_id: d.strftime("%Y-%m-%d"), - '%s-clear_returning_item' % form_id: "1" }) - self.assertEquals(r.status_code, 200) - - # check that it moved below the right header in the DOM - d_header_pos = r.content.find("IESG telechat %s" % d.strftime("%Y-%m-%d")) - draft_pos = r.content.find(draft.filename) - self.assertTrue(d_header_pos < draft_pos) - - draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6") - self.assertEquals(draft.idinternal.telechat_date, d) - self.assertTrue(not draft.idinternal.returning_item) - self.assertEquals(draft.idinternal.comments().count(), comments_before + 1) - self.assertTrue("Telechat" in draft.idinternal.comments()[0].comment_text) - -class RescheduleOnAgendaTestCaseREDESIGN(django.test.TestCase): - fixtures = ['names'] - def test_reschedule(self): from ietf.utils.test_data import make_test_data from ietf.person.models import Person @@ -107,214 +63,7 @@ class RescheduleOnAgendaTestCaseREDESIGN(django.test.TestCase): self.assertEquals(draft.docevent_set.count(), events_before + 1) -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - RescheduleOnAgendaTestCase = RescheduleOnAgendaTestCaseREDESIGN - -class ManageTelechatDatesTestCase(django.test.TestCase): - fixtures = ['base', 'draft'] - - def test_set_dates(self): - dates = TelechatDates.objects.all()[0] - url = urlreverse('ietf.iesg.views.telechat_dates') - login_testing_unauthorized(self, "klm", url) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=date1]')), 1) - - # post - new_date = dates.date1 + timedelta(days=7) - - r = self.client.post(url, dict(date1=new_date.isoformat(), - date2=new_date.isoformat(), - date3=new_date.isoformat(), - date4=new_date.isoformat(), - )) - self.assertEquals(r.status_code, 200) - - dates = TelechatDates.objects.all()[0] - self.assertTrue(dates.date1 == new_date) - - def test_rollup_dates(self): - dates = TelechatDates.objects.all()[0] - url = urlreverse('ietf.iesg.views.telechat_dates') - login_testing_unauthorized(self, "klm", url) - - old_date2 = dates.date2 - new_date = dates.date4 + timedelta(days=14) - r = self.client.post(url, dict(rollup_dates="1")) - self.assertEquals(r.status_code, 200) - - dates = TelechatDates.objects.all()[0] - self.assertTrue(dates.date4 == new_date) - self.assertTrue(dates.date1 == old_date2) - -# class ManageTelechatDatesTestCaseREDESIGN(django.test.TestCase): -# fixtures = ['names'] - -# def test_set_dates(self): -# from ietf.utils.test_data import make_test_data -# make_test_data() - -# dates = TelechatDates.objects.all()[0] -# url = urlreverse('ietf.iesg.views.telechat_dates') -# login_testing_unauthorized(self, "secretary", url) - -# # normal get -# r = self.client.get(url) -# self.assertEquals(r.status_code, 200) -# q = PyQuery(r.content) -# self.assertEquals(len(q('form input[name=date1]')), 1) - -# # post -# new_date = dates.date1 + timedelta(days=7) - -# r = self.client.post(url, dict(date1=new_date.isoformat(), -# date2=new_date.isoformat(), -# date3=new_date.isoformat(), -# date4=new_date.isoformat(), -# )) -# self.assertEquals(r.status_code, 200) - -# dates = TelechatDates.objects.all()[0] -# self.assertTrue(dates.date1 == new_date) - -# def test_rollup_dates(self): -# from ietf.utils.test_data import make_test_data -# make_test_data() - -# dates = TelechatDates.objects.all()[0] -# url = urlreverse('ietf.iesg.views.telechat_dates') -# login_testing_unauthorized(self, "secretary", url) - -# old_date2 = dates.date2 -# new_date = dates.date4 + timedelta(days=14) -# r = self.client.post(url, dict(rollup_dates="1")) -# self.assertEquals(r.status_code, 200) - -# dates = TelechatDates.objects.all()[0] -# self.assertTrue(dates.date4 == new_date) -# self.assertTrue(dates.date1 == old_date2) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - #ManageTelechatDatesTestCase = ManageTelechatDatesTestCaseREDESIGN - del ManageTelechatDatesTestCase - -class WorkingGroupActionsTestCase(django.test.TestCase): - fixtures = ['base', 'wgactions'] - - def setUp(self): - super(self.__class__, self).setUp() - - curdir = os.path.dirname(os.path.abspath(__file__)) - self.evaldir = os.path.join(curdir, "testdir") - os.mkdir(self.evaldir) - - src = os.path.join(curdir, "fixtures", "sieve-charter.txt") - shutil.copy(src, self.evaldir) - - settings.IESG_WG_EVALUATION_DIR = self.evaldir - - def tearDown(self): - super(self.__class__, self).tearDown() - shutil.rmtree(self.evaldir) - - - def test_working_group_actions(self): - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "klm", url) - - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - for wga in WGAction.objects.all(): - self.assertTrue(wga.group_acronym.name in r.content) - - self.assertTrue('(sieve)' in r.content) - - def test_delete_wgaction(self): - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - login_testing_unauthorized(self, "klm", url) - - r = self.client.post(url, dict(delete="1")) - self.assertEquals(r.status_code, 302) - self.assertTrue(not WGAction.objects.filter(pk=wga.pk)) - - def test_edit_wgaction(self): - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - login_testing_unauthorized(self, "klm", url) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form select[name=token_name]')), 1) - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - # change - dates = TelechatDates.objects.all()[0] - token_name = IESGLogin.active_iesg().exclude(first_name=wga.token_name)[0].first_name - old = wga.pk - r = self.client.post(url, dict(status_date=dates.date1.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates.date4.isoformat())) - self.assertEquals(r.status_code, 302) - - wga = WGAction.objects.get(pk=old) - self.assertEquals(wga.status_date, dates.date1) - self.assertEquals(wga.token_name, token_name) - self.assertEquals(wga.category, 23) - self.assertEquals(wga.note, "Testing.") - self.assertEquals(wga.telechat_date, dates.date4) - - def test_add_possible_wg(self): - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "klm", url) - - r = self.client.post(url, dict(add="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 302) - - add_url = r['Location'] - r = self.client.get(add_url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue('(sieve)' in r.content) - self.assertEquals(len(q('form select[name=token_name]')), 1) - self.assertEquals(q('form input[name=status_date]')[0].get("value"), "2010-05-07") - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - wgas_before = WGAction.objects.all().count() - dates = TelechatDates.objects.all()[0] - token_name = IESGLogin.active_iesg()[0].first_name - r = self.client.post(add_url, - dict(status_date=dates.date1.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates.date4.isoformat())) - self.assertEquals(r.status_code, 302) - self.assertEquals(wgas_before + 1, WGAction.objects.all().count()) - - def test_delete_possible_wg(self): - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "klm", url) - - r = self.client.post(url, dict(delete="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 200) - - self.assertTrue('(sieve)' not in r.content) - class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): - fixtures = ['names'] - def setUp(self): super(self.__class__, self).setUp() @@ -454,9 +203,6 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): self.assertEquals(r.status_code, 200) self.assertTrue('(sieve)' not in r.content) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - WorkingGroupActionsTestCase = WorkingGroupActionsTestCaseREDESIGN class IesgUrlTestCase(SimpleUrlTestCase): @@ -473,9 +219,6 @@ class IesgUrlTestCase(SimpleUrlTestCase): from ietf.doc.models import Document,TelechatDocEvent,State from ietf.group.models import Person class DeferUndeferTestCase(django.test.TestCase): - - fixtures=['names'] - def helper_test_defer(self,name): doc = Document.objects.get(name=name) diff --git a/ietf/liaisons/tests.py b/ietf/liaisons/tests.py index e93c1f600..63981a0c4 100644 --- a/ietf/liaisons/tests.py +++ b/ietf/liaisons/tests.py @@ -91,9 +91,7 @@ def make_liaison_models(): return l -class LiaisonManagementTestCase(django.test.TestCase): - fixtures = ['names'] - +class LiaisonManagementTests(django.test.TestCase): def setUp(self): self.liaison_dir = os.path.abspath("tmp-liaison-dir") os.mkdir(self.liaison_dir) diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 0907f27a6..e921cb8cc 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -25,7 +25,7 @@ from ietf.nomcom.utils import get_nomcom_by_year class NomcomViewsTest(TestCase): """Tests to create a new nomcom""" - fixtures = ['names', 'nomcom_templates'] + fixtures = ['nomcom_templates'] def check_url_status(self, url, status): response = self.client.get(url) @@ -589,7 +589,7 @@ class NomcomViewsTest(TestCase): class NomineePositionStateSaveTest(TestCase): """Tests for the NomineePosition save override method""" - fixtures = ['names', 'nomcom_templates'] + fixtures = ['nomcom_templates'] def setUp(self): nomcom_test_data() @@ -621,7 +621,7 @@ class NomineePositionStateSaveTest(TestCase): class FeedbackTest(TestCase): - fixtures = ['names', 'nomcom_templates'] + fixtures = ['nomcom_templates'] def setUp(self): nomcom_test_data() diff --git a/ietf/secr/announcement/tests.py b/ietf/secr/announcement/tests.py index 93accb01e..3a5848c9d 100644 --- a/ietf/secr/announcement/tests.py +++ b/ietf/secr/announcement/tests.py @@ -22,8 +22,6 @@ AD_USER='' class MainTestCase(TestCase): - fixtures = ['names'] - # ------- Test View -------- # def test_main(self): "Main Test" @@ -37,8 +35,6 @@ class DummyCase(TestCase): print name class UnauthorizedCase(TestCase): - fixtures = ['names'] - def test_unauthorized(self): "Unauthorized Test" draft = make_test_data() @@ -49,8 +45,6 @@ class UnauthorizedCase(TestCase): self.assertEquals(r.status_code, 403) class SubmitCase(TestCase): - fixtures = ['names'] - def test_invalid_submit(self): "Invalid Submit" draft = make_test_data() diff --git a/ietf/secr/areas/tests.py b/ietf/secr/areas/tests.py index d19219aa2..47d46c767 100644 --- a/ietf/secr/areas/tests.py +++ b/ietf/secr/areas/tests.py @@ -20,8 +20,6 @@ def augment_data(): by_id=0) class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" draft = make_test_data() diff --git a/ietf/secr/drafts/tests.py b/ietf/secr/drafts/tests.py index a169c89d9..eb54a1afc 100644 --- a/ietf/secr/drafts/tests.py +++ b/ietf/secr/drafts/tests.py @@ -9,8 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" draft = make_test_data() @@ -24,4 +22,4 @@ class MainTestCase(TestCase): drafts = Document.objects.filter(type='draft') url = reverse('drafts_view', kwargs={'id':drafts[0].name}) response = self.client.get(url, REMOTE_USER=SECR_USER) - self.assertEquals(response.status_code, 200) \ No newline at end of file + self.assertEquals(response.status_code, 200) diff --git a/ietf/secr/groups/tests.py b/ietf/secr/groups/tests.py index 12b217bd9..2981cef65 100644 --- a/ietf/secr/groups/tests.py +++ b/ietf/secr/groups/tests.py @@ -8,7 +8,6 @@ import debug SECR_USER='secretary' class GroupsTest(TestCase): - fixtures = ['names'] """ fixtures = [ 'acronym.json', 'area.json', diff --git a/ietf/secr/ipradmin/tests.py b/ietf/secr/ipradmin/tests.py index 5fdb3520a..07a6a9597 100644 --- a/ietf/secr/ipradmin/tests.py +++ b/ietf/secr/ipradmin/tests.py @@ -9,8 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" draft = make_test_data() @@ -25,4 +23,4 @@ class MainTestCase(TestCase): url = reverse('drafts_view', kwargs={'id':drafts[0].name}) response = self.client.get(url, REMOTE_USER=SECR_USER) self.assertEquals(response.status_code, 200) -""" \ No newline at end of file +""" diff --git a/ietf/secr/meetings/tests.py b/ietf/secr/meetings/tests.py index 223107590..61b800635 100644 --- a/ietf/secr/meetings/tests.py +++ b/ietf/secr/meetings/tests.py @@ -9,8 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" url = reverse('meetings') diff --git a/ietf/secr/proceedings/tests.py b/ietf/secr/proceedings/tests.py index b3c1006f4..6ecf53872 100644 --- a/ietf/secr/proceedings/tests.py +++ b/ietf/secr/proceedings/tests.py @@ -10,8 +10,6 @@ import debug SECR_USER='secretary' class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" make_test_data() diff --git a/ietf/secr/roles/tests.py b/ietf/secr/roles/tests.py index e4ebf5df6..4232baf38 100644 --- a/ietf/secr/roles/tests.py +++ b/ietf/secr/roles/tests.py @@ -14,8 +14,6 @@ def augment_data(): Group.objects.create(acronym='dummy',name='Dummy Group',type_id='sdo') class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" augment_data() diff --git a/ietf/secr/rolodex/tests.py b/ietf/secr/rolodex/tests.py index 31b661602..0b8bd58c8 100644 --- a/ietf/secr/rolodex/tests.py +++ b/ietf/secr/rolodex/tests.py @@ -9,8 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" url = reverse('rolodex') diff --git a/ietf/secr/sreq/tests.py b/ietf/secr/sreq/tests.py index 23ae5958e..a093a8acc 100644 --- a/ietf/secr/sreq/tests.py +++ b/ietf/secr/sreq/tests.py @@ -20,8 +20,6 @@ class SreqUrlTestCase(SimpleUrlTestCase): self.doTestUrls(__file__) class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): draft = make_test_data() url = reverse('sessions') @@ -33,8 +31,6 @@ class MainTestCase(TestCase): self.failUnless(len(unsched) > 0) class SubmitRequestCase(TestCase): - fixtures = ['names'] - def test_submit_request(self): draft = make_test_data() acronym = Group.objects.all()[0].acronym @@ -70,4 +66,4 @@ class RetrievePreviousCase(TestCase): # test error if already scheduled # test get previous exists/doesn't exist # test that groups scheduled and unscheduled add up to total groups - # test locking function, access by unauthorized \ No newline at end of file + # test locking function, access by unauthorized diff --git a/ietf/secr/telechat/tests.py b/ietf/secr/telechat/tests.py index e9e0eea99..dd60a68ef 100644 --- a/ietf/secr/telechat/tests.py +++ b/ietf/secr/telechat/tests.py @@ -15,8 +15,6 @@ def augment_data(): TelechatDate.objects.create(date=datetime.datetime.today()) class MainTestCase(TestCase): - fixtures = ['names'] - def test_main(self): "Main Test" augment_data() diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index 11deee1cf..d7b528eea 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -19,8 +19,8 @@ from ietf.group.models import Group, Role from ietf.doc.models import * from ietf.submit.models import IdSubmissionDetail, Preapproval -class SubmitTestCase(django.test.TestCase): - fixtures = ['names', 'idsubmissionstatus'] +class SubmitTests(django.test.TestCase): + fixtures = ['idsubmissionstatus'] def setUp(self): self.staging_dir = os.path.abspath("tmp-submit-staging-dir") @@ -401,8 +401,8 @@ class SubmitTestCase(django.test.TestCase): self.assertTrue("Full URL for managing submission" in outbox[-1]["Subject"]) self.assertTrue(name in outbox[-1]["Subject"]) -class ApprovalsTestCase(django.test.TestCase): - fixtures = ['names', 'idsubmissionstatus'] +class ApprovalsTests(django.test.TestCase): + fixtures = ['idsubmissionstatus'] def test_approvals(self): make_test_data() diff --git a/ietf/sync/tests.py b/ietf/sync/tests.py index 1814586ae..6b4243cdd 100644 --- a/ietf/sync/tests.py +++ b/ietf/sync/tests.py @@ -15,9 +15,7 @@ from ietf.sync import iana, rfceditor from pyquery import PyQuery -class IANASyncTestCase(django.test.TestCase): - fixtures = ['names'] - +class IANASyncTests(django.test.TestCase): def test_protocol_page_sync(self): draft = make_test_data() DocAlias.objects.create(name="rfc1234", document=draft) @@ -176,9 +174,7 @@ ICANN self.assertEqual(DocEvent.objects.filter(doc=draft, type="iana_review").count(), 1) -class RFCSyncTestCase(django.test.TestCase): - fixtures = ['names'] - +class RFCSyncTests(django.test.TestCase): def test_rfc_index(self): doc = make_test_data() doc.set_state(State.objects.get(used=True, type="draft-iesg", slug="rfcqueue")) @@ -367,9 +363,7 @@ class RFCSyncTestCase(django.test.TestCase): self.assertEquals(len(changed), 0) self.assertEquals(len(warnings), 0) -class DiscrepanciesTestCase(django.test.TestCase): - fixtures = ['names'] - +class DiscrepanciesTests(django.test.TestCase): def test_discrepancies(self): make_test_data() @@ -408,9 +402,7 @@ class DiscrepanciesTestCase(django.test.TestCase): r = self.client.get(urlreverse("ietf.sync.views.discrepancies")) self.assertTrue(doc.name in r.content) -class RFCEditorUndoTestCase(django.test.TestCase): - fixtures = ['names'] - +class RFCEditorUndoTests(django.test.TestCase): def test_rfceditor_undo(self): draft = make_test_data() diff --git a/ietf/wgcharter/tests.py b/ietf/wgcharter/tests.py index b6208a631..721dfb5fd 100644 --- a/ietf/wgcharter/tests.py +++ b/ietf/wgcharter/tests.py @@ -21,9 +21,7 @@ from ietf.person.models import * from ietf.iesg.models import TelechatDate from ietf.wgcharter.utils import * -class EditCharterTestCase(django.test.TestCase): - fixtures = ['names'] - +class EditCharterTests(django.test.TestCase): def setUp(self): self.charter_dir = os.path.abspath("tmp-charter-dir") os.mkdir(self.charter_dir) @@ -197,9 +195,7 @@ class EditCharterTestCase(django.test.TestCase): self.assertEquals(f.read(), "Windows line\nMac line\nUnix line\n" + utf_8_snippet) -class ApproveCharterTestCase(django.test.TestCase): - fixtures = ['names'] - +class ApproveCharterTests(django.test.TestCase): def setUp(self): self.charter_dir = os.path.abspath("tmp-charter-dir") os.mkdir(self.charter_dir) diff --git a/ietf/wginfo/tests.py b/ietf/wginfo/tests.py index 33134b233..f42c16770 100644 --- a/ietf/wginfo/tests.py +++ b/ietf/wginfo/tests.py @@ -51,8 +51,6 @@ from ietf.person.models import * from ietf.wginfo.mails import * class GroupPagesTests(django.test.TestCase): - fixtures = ["names"] - def setUp(self): self.charter_dir = os.path.abspath("tmp-charter-dir") os.mkdir(self.charter_dir) @@ -213,9 +211,7 @@ class GroupPagesTests(django.test.TestCase): self.assertEquals(r.status_code, 200) self.assertTrue(e.desc in r.content) -class WgEditTestCase(django.test.TestCase): - fixtures = ["names"] - +class WgEditTests(django.test.TestCase): def setUp(self): self.charter_dir = os.path.abspath("tmp-charter-dir") os.mkdir(self.charter_dir) @@ -413,9 +409,7 @@ class WgEditTestCase(django.test.TestCase): group = Group.objects.get(acronym=group.acronym) self.assertEquals(group.state_id, "active") -class MilestoneTestCase(django.test.TestCase): - fixtures = ["names"] - +class MilestoneTests(django.test.TestCase): def create_test_milestones(self): draft = make_test_data() @@ -820,9 +814,7 @@ class MilestoneTestCase(django.test.TestCase): self.assertTrue(m1.desc in unicode(outbox[-1])) self.assertTrue(m2.desc in unicode(outbox[-1])) -class CustomizeWorkflowTestCase(django.test.TestCase): - fixtures = ['names'] - +class CustomizeWorkflowTests(django.test.TestCase): def test_customize_workflow(self): make_test_data() From 52febb2fc0474a28fc6a74a27b6ffe021053982a Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 30 Sep 2013 12:17:15 +0000 Subject: [PATCH 051/173] Fix spelling bug in global[e] test fixtures - Legacy-Id: 6311 --- ietf/utils/test_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/utils/test_runner.py b/ietf/utils/test_runner.py index 2a5fb9fdf..7ed1c753a 100644 --- a/ietf/utils/test_runner.py +++ b/ietf/utils/test_runner.py @@ -57,7 +57,7 @@ def safe_create_1(self, verbosity, *args, **kwargs): print " Saving test database name "+settings.DATABASES["default"]["NAME"]+"..." test_database_name = settings.DATABASES["default"]["NAME"] if settings.TEST_GLOBAL_FIXTURES: - print " Loading globale test fixtures: %s" % ", ".join(settings.TEST_GLOBAL_FIXTURES) + print " Loading global test fixtures: %s" % ", ".join(settings.TEST_GLOBAL_FIXTURES) call_command('loaddata', *settings.TEST_GLOBAL_FIXTURES, verbosity=0, commit=False, database="default") return x From 5c6ee01bf74aafdc0aa4024397b14164d713741c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 30 Sep 2013 12:17:38 +0000 Subject: [PATCH 052/173] drafts -> docs in /feed/iesg-agenda/ since we're returning more than just I-Ds, sort by latest first, limit to documents on current and future telechats (as the description says) to speed it up, add simple test of the feed - Legacy-Id: 6312 --- ietf/iesg/feeds.py | 34 +++++++++++++++++-------------- ietf/iesg/tests.py | 51 ++++++++++++++++++++++++---------------------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/ietf/iesg/feeds.py b/ietf/iesg/feeds.py index 72c77ebd1..a17441f03 100644 --- a/ietf/iesg/feeds.py +++ b/ietf/iesg/feeds.py @@ -8,27 +8,31 @@ import datetime class IESGAgenda(Feed): title = "Documents on Future IESG Telechat Agendas" - link = "http://datatracker.ietf.org/iesg/agenda/" + link = settings.IDTRACKER_BASE_URL + "/iesg/agenda/" feed_type = Atom1Feed def items(self): from ietf.doc.models import TelechatDocEvent - drafts = Document.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.min).distinct() - for d in drafts: + docs = Document.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.today()).distinct() + for d in docs: d.latest_telechat_event = d.latest_event(TelechatDocEvent, type="scheduled_for_telechat") - drafts = [d for d in drafts if d.latest_telechat_event.telechat_date] - drafts.sort(key=lambda d: d.latest_telechat_event.telechat_date) - return drafts + docs = [d for d in docs if d.latest_telechat_event.telechat_date] + docs.sort(key=lambda d: d.latest_telechat_event.telechat_date, reverse=True) + return docs + def item_categories(self, doc): + return [ str(doc.telechat_date) ] - def item_categories(self, item): - return [ str(item.telechat_date) ] - - def item_pubdate(self, item): - return item.latest_telechat_event.time + def item_pubdate(self, doc): + return doc.latest_telechat_event.time - def item_author_name(self, item): - return str( item.ad ) if item.ad else "None" + def item_author_name(self, doc): + return doc.ad.plain_name() if doc.ad else "None" - def item_author_email(self, item): - return str( item.ad.role_email("ad") ) if item.ad else "" + def item_author_email(self, doc): + if not doc.ad: + return "" + e = doc.ad.role_email("ad") + if not e: + return "" + return e.address diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index a6302338e..2eaec76f3 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -1,4 +1,3 @@ -from datetime import timedelta import os, shutil import django.test @@ -7,16 +6,38 @@ from django.conf import settings from pyquery import PyQuery -from ietf.idtracker.models import * +from ietf.utils.test_data import make_test_data +from ietf.doc.models import Document, TelechatDocEvent, State +from ietf.person.models import Person +from ietf.group.models import Group from ietf.iesg.models import * from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized +class IESGAgendaTests(django.test.TestCase): + def test_feed(self): + draft = make_test_data() + + url = "/feed/iesg-agenda/" + + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(draft.name not in r.content) + + # add to schedule + e = TelechatDocEvent(type="scheduled_for_telechat") + e.doc = draft + e.by = Person.objects.get(name="Aread Irector") + e.telechat_date = TelechatDate.objects.active()[0].date + e.returning_item = True + e.save() + + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(draft.name in r.content) + self.assertTrue(draft.title in r.content) + class RescheduleOnAgendaTestCase(django.test.TestCase): def test_reschedule(self): - from ietf.utils.test_data import make_test_data - from ietf.person.models import Person - from ietf.doc.models import TelechatDocEvent - draft = make_test_data() # add to schedule @@ -82,8 +103,6 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): def test_working_group_actions(self): - from ietf.utils.test_data import make_test_data - make_test_data() url = urlreverse('iesg_working_group_actions') @@ -98,8 +117,6 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): self.assertTrue('(sieve)' in r.content) def test_delete_wgaction(self): - from ietf.utils.test_data import make_test_data - make_test_data() wga = WGAction.objects.all()[0] @@ -111,9 +128,6 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): self.assertTrue(not WGAction.objects.filter(pk=wga.pk)) def test_edit_wgaction(self): - from ietf.utils.test_data import make_test_data - from ietf.person.models import Person - make_test_data() wga = WGAction.objects.all()[0] @@ -146,10 +160,6 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): self.assertEquals(wga.telechat_date, dates[3].date) def test_add_possible_wg(self): - from ietf.utils.test_data import make_test_data - from ietf.person.models import Person - from ietf.group.models import Group - make_test_data() url = urlreverse('iesg_working_group_actions') @@ -191,8 +201,6 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): self.assertEquals(wgas_before + 1, WGAction.objects.all().count()) def test_delete_possible_wg(self): - from ietf.utils.test_data import make_test_data - make_test_data() url = urlreverse('iesg_working_group_actions') @@ -214,10 +222,6 @@ class IesgUrlTestCase(SimpleUrlTestCase): else: return content -#Tests added since database redesign that speak the new clases - -from ietf.doc.models import Document,TelechatDocEvent,State -from ietf.group.models import Person class DeferUndeferTestCase(django.test.TestCase): def helper_test_defer(self,name): @@ -313,5 +317,4 @@ class DeferUndeferTestCase(django.test.TestCase): # when charters support being deferred, be sure to test them here def setUp(self): - from ietf.utils.test_data import make_test_data make_test_data() From d9ccff188952bfe363ce9a72b4febf251b1ef16f Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 30 Sep 2013 12:49:08 +0000 Subject: [PATCH 053/173] Get rid of the view behind /iesg/ann/0-9+/ (apparently for displaying ballot text for drafts processed by the IESG) - it has not worked since the switch to the new schema as it's based on the old numeric primary keys which we no longer have - Legacy-Id: 6313 --- ietf/iesg/urls.py | 46 +++++++++------------- ietf/templates/iesg/ballotinfo_detail.html | 33 ---------------- 2 files changed, 18 insertions(+), 61 deletions(-) delete mode 100644 ietf/templates/iesg/ballotinfo_detail.html diff --git a/ietf/iesg/urls.py b/ietf/iesg/urls.py index aa6532e13..fd1578b5b 100644 --- a/ietf/iesg/urls.py +++ b/ietf/iesg/urls.py @@ -35,36 +35,26 @@ from django.conf.urls.defaults import patterns, url from django.conf import settings from ietf.iesg import views -from ietf.idtracker.models import BallotInfo - -queryset_ann = BallotInfo.objects.all() urlpatterns = patterns('', - (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }) -) - -urlpatterns += patterns('django.views.generic.list_detail', - (r'^ann/(?P\d+)/$', 'object_detail', { 'queryset': queryset_ann, 'template_name':"iesg/ballotinfo_detail.html" }), -) - -urlpatterns += patterns('', - (r'^ann/ind/$',views.inddocs), - (r'^ann/(?P[^/]+)/$',views.wgdocs), - (r'^agenda/$', views.agenda), - (r'^agenda/agenda.txt$', views.agenda_txt), - (r'^agenda/agenda.json$', views.agenda_json), - (r'^agenda/scribe_template.html$', views.agenda_scribe_template), - (r'^agenda/moderator_package.html$', views.agenda_moderator_package), - (r'^agenda/agenda_package.txt$', views.agenda_package), - (r'^agenda/documents.txt$', views.agenda_documents_txt), - (r'^agenda/documents/$', views.agenda_documents), - (r'^agenda/telechat-(?P\d+)-(?P\d+)-(?P\d+)-docs.tgz', views.telechat_docs_tarfile), - (r'^discusses/$', views.discusses), - (r'^milestones', views.milestones_needing_review), - (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), - url(r'^wgactions/$', views.working_group_actions, name="iesg_working_group_actions"), - url(r'^wgactions/add/$', views.edit_working_group_action, { 'wga_id': None }, name="iesg_add_working_group_action"), - url(r'^wgactions/(?P\d+)/$', views.edit_working_group_action, name="iesg_edit_working_group_action"), + (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }) + (r'^ann/ind/$',views.inddocs), + (r'^ann/(?P[^/]+)/$',views.wgdocs), + (r'^agenda/$', views.agenda), + (r'^agenda/agenda.txt$', views.agenda_txt), + (r'^agenda/agenda.json$', views.agenda_json), + (r'^agenda/scribe_template.html$', views.agenda_scribe_template), + (r'^agenda/moderator_package.html$', views.agenda_moderator_package), + (r'^agenda/agenda_package.txt$', views.agenda_package), + (r'^agenda/documents.txt$', views.agenda_documents_txt), + (r'^agenda/documents/$', views.agenda_documents), + (r'^agenda/telechat-(?P\d+)-(?P\d+)-(?P\d+)-docs.tgz', views.telechat_docs_tarfile), + (r'^discusses/$', views.discusses), + (r'^milestones', views.milestones_needing_review), + (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), + url(r'^wgactions/$', views.working_group_actions, name="iesg_working_group_actions"), + url(r'^wgactions/add/$', views.edit_working_group_action, { 'wga_id': None }, name="iesg_add_working_group_action"), + url(r'^wgactions/(?P\d+)/$', views.edit_working_group_action, name="iesg_edit_working_group_action"), ) if settings.SERVER_MODE != 'production': diff --git a/ietf/templates/iesg/ballotinfo_detail.html b/ietf/templates/iesg/ballotinfo_detail.html deleted file mode 100644 index f9d30e634..000000000 --- a/ietf/templates/iesg/ballotinfo_detail.html +++ /dev/null @@ -1,33 +0,0 @@ -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% extends "base.html" %} -{% block title %}IESG Announcement{% endblock %} - -{% block content %} -

      IESG Announcement

      -
      -

      - This page contains an IESG Protocol, Document, or Working Group Action - announcement. Other announcements can be found using the links below. -

      - - -
        -
      1. Protocol Action Announcements
      2. -
      3. Document Action Announcements
      4. -
      5. Working Group Action Announcements
      6. -
      7. Recent IESG Announcements
      8. -
      9. Previous IESG Announcements
      10. -
      -
      -
      -
      -
      -{{ object.approval_text|escape|urlize }}
      -
      -{{ object.ballot_writeup|escape|urlize }}
      -
      - - - -{% endblock %} - From b552ff31aafcd2af21b3d3dfeb952ab84bfe719d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 2 Oct 2013 15:37:44 +0000 Subject: [PATCH 054/173] Replace the announcement pages with a new page collecting all IESG review decisions. Add migration to split up iesg_approve/disapprove events of the form "IESG has approved ... and state has been changed ..." into the approve/disapprove event and a synthesized state change event. Also regularize the descriptions a bit. This simplifies the code in the new page. - Legacy-Id: 6340 --- ietf/bin/test-crawl | 2 +- ...plit_state_changing_iesg_approve_events.py | 435 ++++++++++++++++++ ietf/doc/views_ballot.py | 2 +- ietf/iesg/urls.py | 8 +- ietf/iesg/views.py | 94 +--- ietf/templates/iesg/ietf_doc.html | 71 --- ietf/templates/iesg/independent_doc.html | 44 -- ietf/templates/iesg/review_decisions.html | 19 + 8 files changed, 482 insertions(+), 193 deletions(-) create mode 100644 ietf/doc/migrations/0015_split_state_changing_iesg_approve_events.py delete mode 100644 ietf/templates/iesg/ietf_doc.html delete mode 100644 ietf/templates/iesg/independent_doc.html create mode 100644 ietf/templates/iesg/review_decisions.html diff --git a/ietf/bin/test-crawl b/ietf/bin/test-crawl index 405be9341..3c71f01f7 100755 --- a/ietf/bin/test-crawl +++ b/ietf/bin/test-crawl @@ -24,7 +24,7 @@ connection.queries = DontSaveQueries() MAX_URL_LENGTH = 500 SLOW_THRESHOLD = 1.0 -initial = ["/doc/all/", "/doc/in-last-call/"] +initial = ["/doc/all/", "/doc/in-last-call/", "/iesg/decisions/"] visited = set() urls = {} # url -> referrer diff --git a/ietf/doc/migrations/0015_split_state_changing_iesg_approve_events.py b/ietf/doc/migrations/0015_split_state_changing_iesg_approve_events.py new file mode 100644 index 000000000..f12b880d9 --- /dev/null +++ b/ietf/doc/migrations/0015_split_state_changing_iesg_approve_events.py @@ -0,0 +1,435 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + state_mapping = dict((s.name.lower(), s) for s in orm.State.objects.filter(type="draft-iesg")) + doc_type_mapping = { + 'draft': " the document", + 'conflrev': " the conflict review response", + 'statchg': " the status change", + 'charter': " the charter", + } + + import re + approve_re = re.compile(r"IESG has approved( the charter| the document| the conflict review response| the status change)?( and state has been changed to '?(.*?)'?( by .*)?\.?)?$") + disapprove_re = re.compile("(DNP|Do Not Publish) note has been sent to( the)? RFC Editor( and state has been changed to '?(.*?)'?( by .*)?\.?)?$") + for e in orm.DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved")).order_by("id").select_related("doc").iterator(): + if e.type == "iesg_approved": + m = approve_re.match(e.desc) + if not m: + print "FAIL", e.desc + else: + doc_type, state_set, state_name, by = m.groups() + + if state_set: + state = state_mapping[state_name.lower()] + + s = orm.StateDocEvent() + s.doc_id = e.doc_id + s.by_id = e.by_id + s.type = "changed_state" + s.state_type_id = state.type_id + s.state = state + s.desc = "%s changed to %s" % (state.type.label, state.name) + s.time = e.time + s.save() + + #print e.doc_id, s.desc + + if not doc_type: + doc_type = doc_type_mapping[e.doc.type_id] + + new_desc = "IESG has approved%s" % doc_type + + if new_desc != e.desc: + #print e.doc_id, e.desc, "->", new_desc + orm.DocEvent.objects.filter(id=e.id).update(desc=new_desc) + + elif e.type == "iesg_disapproved": + m = disapprove_re.match(e.desc) + if not m: + print "FAIL", e.desc + else: + dnp, the, state_set, state_name, by = m.groups() + + if state_set: + state = state_mapping[state_name.lower()] + + s = orm.StateDocEvent() + s.doc_id = e.doc_id + s.by_id = e.by_id + s.type = "changed_state" + s.state_type_id = state.type_id + s.state = state + s.desc = "%s changed to %s" % (state.type.label, state.name) + s.time = e.time + s.save() + + #print e.doc_id, s.desc + + new_desc = "Do Not Publish note has been sent to the RFC Editor" + + if new_desc != e.desc: + #print e.doc_id, e.desc, "->", new_desc + orm.DocEvent.objects.filter(id=e.id).update(desc=new_desc) + + + def backwards(self, orm): + "Write your backwards methods here." + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.ballotdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']}, + 'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'doc.ballotpositiondocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']}, + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['doc.BallotDocEvent']", 'null': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"}) + }, + 'doc.ballottype': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotType'}, + 'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}), + 'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.consensusdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': ['doc.DocEvent']}, + 'consensus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'doc.deletedevent': { + 'Meta': {'object_name': 'DeletedEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'doc.docalias': { + 'Meta': {'object_name': 'DocAlias'}, + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + 'doc.docevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'doc.dochistory': { + 'Meta': {'object_name': 'DocHistory'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}), + 'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.dochistoryauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {}) + }, + 'doc.docreminder': { + 'Meta': {'object_name': 'DocReminder'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'due': ('django.db.models.fields.DateTimeField', [], {}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.initialreviewdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'doc.lastcalldocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'doc.newrevisiondocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + 'doc.relateddochistory': { + 'Meta': {'object_name': 'RelatedDocHistory'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"}) + }, + 'doc.relateddocument': { + 'Meta': {'object_name': 'RelatedDocument'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statedocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.State']", 'null': 'True', 'blank': 'True'}), + 'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'doc.telechatdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) + }, + 'doc.writeupdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['doc'] diff --git a/ietf/doc/views_ballot.py b/ietf/doc/views_ballot.py index 2d3297f39..3c87d7527 100644 --- a/ietf/doc/views_ballot.py +++ b/ietf/doc/views_ballot.py @@ -693,7 +693,7 @@ def approve_ballot(request, name): e = DocEvent(doc=doc, by=login) if action == "do_not_publish": e.type = "iesg_disapproved" - e.desc = "Do Not Publish note has been sent to RFC Editor" + e.desc = "Do Not Publish note has been sent to the RFC Editor" else: e.type = "iesg_approved" e.desc = "IESG has approved the document" diff --git a/ietf/iesg/urls.py b/ietf/iesg/urls.py index fd1578b5b..21c0c4384 100644 --- a/ietf/iesg/urls.py +++ b/ietf/iesg/urls.py @@ -37,10 +37,10 @@ from django.conf import settings from ietf.iesg import views urlpatterns = patterns('', - (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }) - (r'^ann/ind/$',views.inddocs), - (r'^ann/(?P[^/]+)/$',views.wgdocs), - (r'^agenda/$', views.agenda), + (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }), + (r'^decisions/(?:(?P[0-9]{4})/)?$', views.review_decisions), + (r'^ann/(?:ind|new|prev)/$', 'django.views.generic.simple.redirect_to', { 'url': "/iesg/decisions/", 'permanent': True }), + (r'^agenda/$', views.agenda), (r'^agenda/agenda.txt$', views.agenda_txt), (r'^agenda/agenda.json$', views.agenda_json), (r'^agenda/scribe_template.html$', views.agenda_scribe_template), diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 8fb253aa6..b63048eeb 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -36,7 +36,6 @@ import codecs, re, os, glob import datetime import tarfile -from ietf.idtracker.models import IDInternal, InternetDraft, AreaGroup, Position, IESGLogin, Acronym from django.views.generic.list_detail import object_list from django.views.generic.simple import direct_to_template from django.views.decorators.vary import vary_on_cookie @@ -47,91 +46,42 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.conf import settings from django.utils import simplejson as json from django import forms + +from ietf.idtracker.models import IDInternal, InternetDraft, AreaGroup, Position, IESGLogin, Acronym + from ietf.iesg.models import TelechatDates, TelechatAgendaItem, WGAction from ietf.idrfc.idrfc_wrapper import IdWrapper, RfcWrapper from ietf.doc.utils import update_telechat from ietf.ietfauth.decorators import group_required, role_required from ietf.ietfauth.utils import has_role from ietf.ipr.models import IprDocAlias -from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent +from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent from ietf.group.models import Group, GroupMilestone -def date_threshold(): - """Return the first day of the month that is 185 days ago.""" - ret = datetime.date.today() - datetime.timedelta(days=185) - ret = ret - datetime.timedelta(days=ret.day - 1) - return ret +def review_decisions(request, year=None): + events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved")) -def inddocs(request): - queryset_list_ind = [d for d in InternetDraft.objects.filter(stream__in=("IRTF","ISE"), docevent__type="iesg_approved").distinct() if d.latest_event(type__in=("iesg_disapproved", "iesg_approved")).type == "iesg_approved"] - queryset_list_ind.sort(key=lambda d: d.b_approve_date, reverse=True) + years = sorted((d.year for d in events.dates('time', 'year')), reverse=True) - queryset_list_ind_dnp = [d for d in IDInternal.objects.filter(stream__in=("IRTF","ISE"), docevent__type="iesg_disapproved").distinct() if d.latest_event(type__in=("iesg_disapproved", "iesg_approved")).type == "iesg_disapproved"] - queryset_list_ind_dnp.sort(key=lambda d: d.dnp_date, reverse=True) - - return render_to_response('iesg/independent_doc.html', - dict(object_list=queryset_list_ind, - object_list_dnp=queryset_list_ind_dnp), - context_instance=RequestContext(request)) - - -def wgdocs(request,cat): - pass - -def wgdocsREDESIGN(request,cat): - is_recent = 0 - proto_actions = [] - doc_actions = [] - threshold = date_threshold() - - proto_levels = ["bcp", "ds", "ps", "std"] - doc_levels = ["exp", "inf"] - - if cat == 'new': - is_recent = 1 - - drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__gte=threshold, intended_std_level__in=proto_levels + doc_levels).exclude(stream__in=("ISE","IRTF")).distinct() - for d in drafts: - if d.b_approve_date and d.b_approve_date >= threshold: - if d.intended_std_level_id in proto_levels: - proto_actions.append(d) - elif d.intended_std_level_id in doc_levels: - doc_actions.append(d) - - elif cat == 'prev': - # proto - start_date = datetime.date(1997, 12, 1) - - drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__lt=threshold, docevent__time__gte=start_date, intended_std_level__in=proto_levels).exclude(stream__in=("ISE","IRTF")).distinct() - - for d in drafts: - if d.b_approve_date and start_date <= d.b_approve_date < threshold: - proto_actions.append(d) - - # doc - start_date = datetime.date(1998, 10, 15) - - drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__lt=threshold, docevent__time__gte=start_date, intended_std_level__in=doc_levels).exclude(stream__in=("ISE","IRTF")).distinct() - - for d in drafts: - if d.b_approve_date and start_date <= d.b_approve_date < threshold: - doc_actions.append(d) + if year: + year = int(year) + events = events.filter(time__year=year) else: - raise Http404 + d = datetime.date.today() - datetime.timedelta(days=185) + d = datetime.date(d.year, d.month, 1) + events = events.filter(time__gte=d) - proto_actions.sort(key=lambda d: d.b_approve_date, reverse=True) - doc_actions.sort(key=lambda d: d.b_approve_date, reverse=True) - - return render_to_response('iesg/ietf_doc.html', - dict(object_list=proto_actions, - object_list_doc=doc_actions, - is_recent=is_recent, - title_prefix="Recent" if is_recent else "Previous"), + events = events.select_related("doc", "doc__intended_std_level").order_by("-time", "-id") + + #proto_levels = ["bcp", "ds", "ps", "std"] + #doc_levels = ["exp", "inf"] + + return render_to_response('iesg/review_decisions.html', + dict(events=events, + years=years, + year=year), context_instance=RequestContext(request)) -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - wgdocs = wgdocsREDESIGN - def get_doc_section(id): pass diff --git a/ietf/templates/iesg/ietf_doc.html b/ietf/templates/iesg/ietf_doc.html deleted file mode 100644 index a16da6e77..000000000 --- a/ietf/templates/iesg/ietf_doc.html +++ /dev/null @@ -1,71 +0,0 @@ -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% extends "base.html" %} -{% block title %}{{ title_prefix }} IESG Announcements{% endblock %} -{% block content %} -{% if is_recent %} -

      Recent IESG Announcements

      -
      -This page contains links to all IESG Protocol, Document, and Working Group Action announcements that have been sent within the past six months. Announcements that were sent prior to six month ago can be found in Previous Announcements.
      -
      - - -1. Protocol Action Announcements
      -2. Document Action Announcements
      -3. Working Group Action Announcements
      -4. Previous Announcements
      -
      -
      -{% else %} -

      Previous IESG Announcements

      -
      -This page contains links to all IESG Protocol, Document, and Working Group Action announcements that were sent prior to six months ago. Announcements that have been sent within the past six months can be found in Recent Announcements. -

      - -1. Protocol Action Announcements
      -2. Document Action Announcements
      -3. Working Group Action Announcements
      -4. Recent IESG Announcements
      -
      -
      -{% endif %} -
      -

      1. Protocol Action Announcements

      -{% regroup object_list by b_approve_date|date:"F j, Y" as dates %} -{% for date in dates %} -{% if date.list.0.idinternal.ballot_id %}Date Sent: {{ date.grouper }} {% endif %} - -{% endfor %} - -

      2. Document Action Announcements

      -{% regroup object_list_doc by b_approve_date|date:"F j, Y" as dates %} -{% for date in dates %} -{% if date.list.0.idinternal.ballot_id %}Date Sent: {{ date.grouper }} {% endif %} - -{% endfor %} - -

      3. Working Group Action Announcements

      -Coming Soon ... - -{% endblock %} diff --git a/ietf/templates/iesg/independent_doc.html b/ietf/templates/iesg/independent_doc.html deleted file mode 100644 index fd63d45ca..000000000 --- a/ietf/templates/iesg/independent_doc.html +++ /dev/null @@ -1,44 +0,0 @@ -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% extends "base.html" %} -{% block title %}IESG Statements on Independent Submissions{% endblock %} -{% block content %} -

      IESG Statements on Independent Submissions

      -The RFC Editor receives requests to publish non-IETF Working Group documents as independent Informational or Experimental RFCs. Following the process defined in RFC 3932, the RFC Editor requests that the IESG review these documents and provide input. This page contains copies of those messages that were sent by the IESG to the RFC Editor following such reviews. -
      - -

      Positive IESG Responses

      - -{% regroup object_list by b_approve_date|date:"F j, Y" as dates %} -{% for date in dates %} -Date Sent: {{ date.grouper }} - -{% endfor %} - - -OLD LIST
      -

      Negative IESG Responses

      -{% regroup object_list_dnp by dnp_date|date:"F j, Y" as dates %} -{% for date in dates %} -Date Sent: {{ date.grouper }} - -{% endfor %} - -
      - -OLD LIST



      -{% endblock %} diff --git a/ietf/templates/iesg/review_decisions.html b/ietf/templates/iesg/review_decisions.html new file mode 100644 index 000000000..cff705dda --- /dev/null +++ b/ietf/templates/iesg/review_decisions.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block title %}IESG Review Decisions{% endblock %} + +{% block content %} +

      IESG Review Decisions

      + +

      Showing review decisions announced in {% if year %}{{ year }}{% else %}the past 6 months{% endif %}.

      + +

      Announcements in: + {% for y in years %}{{ y }}{% if not forloop.last %}, {% endif %}{% endfor %} +

      + +{% for e in events %} +{% ifchanged %}

      {{ e.time|date:"F j, Y" }}

      {% endifchanged %} +
      {{ e.desc }} {{ e.doc.name }} {% if e.doc.intended_std_level %}({{ e.doc.intended_std_level }}){% endif %} - {{ e.doc.title }}
      +{% endfor %} + +{% endblock %} From 66e2503e89dfa7b280f14ce64360b65b84fb106d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 2 Oct 2013 15:38:26 +0000 Subject: [PATCH 055/173] Add db_index on DocEvent.time to speed up queries on historical data - Legacy-Id: 6341 --- .../0016_add_docevent_time_index.py | 370 ++++++++++++++++++ ietf/doc/models.py | 2 +- 2 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 ietf/doc/migrations/0016_add_docevent_time_index.py diff --git a/ietf/doc/migrations/0016_add_docevent_time_index.py b/ietf/doc/migrations/0016_add_docevent_time_index.py new file mode 100644 index 000000000..d77cff9c7 --- /dev/null +++ b/ietf/doc/migrations/0016_add_docevent_time_index.py @@ -0,0 +1,370 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding index on 'DocEvent', fields ['time'] + db.create_index('doc_docevent', ['time']) + + + def backwards(self, orm): + + # Removing index on 'DocEvent', fields ['time'] + db.delete_index('doc_docevent', ['time']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.ballotdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']}, + 'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'doc.ballotpositiondocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']}, + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['doc.BallotDocEvent']", 'null': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"}) + }, + 'doc.ballottype': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotType'}, + 'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}), + 'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.consensusdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': ['doc.DocEvent']}, + 'consensus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'doc.deletedevent': { + 'Meta': {'object_name': 'DeletedEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'doc.docalias': { + 'Meta': {'object_name': 'DocAlias'}, + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + 'doc.docevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'doc.dochistory': { + 'Meta': {'object_name': 'DocHistory'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}), + 'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.dochistoryauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {}) + }, + 'doc.docreminder': { + 'Meta': {'object_name': 'DocReminder'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'due': ('django.db.models.fields.DateTimeField', [], {}), + 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.initialreviewdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'doc.lastcalldocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'doc.newrevisiondocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + 'doc.relateddochistory': { + 'Meta': {'object_name': 'RelatedDocHistory'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"}) + }, + 'doc.relateddocument': { + 'Meta': {'object_name': 'RelatedDocument'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statedocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.State']", 'null': 'True', 'blank': 'True'}), + 'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'doc.telechatdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) + }, + 'doc.writeupdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['doc'] diff --git a/ietf/doc/models.py b/ietf/doc/models.py index ae6cbcac7..7066f7443 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -568,7 +568,7 @@ EVENT_TYPES = [ class DocEvent(models.Model): """An occurrence for a document, used for tracking who, when and what.""" - time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened") + time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened", db_index=True) type = models.CharField(max_length=50, choices=EVENT_TYPES) by = models.ForeignKey(Person) doc = models.ForeignKey('doc.Document') From 43d4017891d5f549230cb28d8f0bdf6cc9d5b60e Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 2 Oct 2013 15:50:29 +0000 Subject: [PATCH 056/173] Get rid of some unnecessary queries on IESG review decision page, fix a couple of issues in the layout - Legacy-Id: 6343 --- ietf/templates/iesg/review_decisions.html | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ietf/templates/iesg/review_decisions.html b/ietf/templates/iesg/review_decisions.html index cff705dda..24e1bc90e 100644 --- a/ietf/templates/iesg/review_decisions.html +++ b/ietf/templates/iesg/review_decisions.html @@ -2,6 +2,11 @@ {% block title %}IESG Review Decisions{% endblock %} +{% block morecss %} +div.decisions h3 { margin-bottom: 0.5em; } +div.decisions div { padding-left: 1em; text-indent: -1em; } +{% endblock %} + {% block content %}

      IESG Review Decisions

      @@ -11,9 +16,11 @@ {% for y in years %}{{ y }}{% if not forloop.last %}, {% endif %}{% endfor %}

      +
      {% for e in events %} -{% ifchanged %}

      {{ e.time|date:"F j, Y" }}

      {% endifchanged %} -
      {{ e.desc }} {{ e.doc.name }} {% if e.doc.intended_std_level %}({{ e.doc.intended_std_level }}){% endif %} - {{ e.doc.title }}
      + {% ifchanged %}

      {{ e.time|date:"F j, Y" }}

      {% endifchanged %} +
      {{ e.desc }} {{ e.doc.name }} {% if e.doc.intended_std_level %}({{ e.doc.intended_std_level }}){% endif %} - {{ e.doc.title }}
      {% endfor %} +
      {% endblock %} From 5fe7ce62ff4663bcb9f21c4932ac1bbd80aceb3f Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 3 Oct 2013 11:41:47 +0000 Subject: [PATCH 057/173] Add timeframe to IESG Review Decisions page - Legacy-Id: 6348 --- ietf/iesg/views.py | 5 ++++- ietf/templates/iesg/review_decisions.html | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index b63048eeb..1d53736cb 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -76,10 +76,13 @@ def review_decisions(request, year=None): #proto_levels = ["bcp", "ds", "ps", "std"] #doc_levels = ["exp", "inf"] + timeframe = u"%s" % year if year else u"the past 6 months" + return render_to_response('iesg/review_decisions.html', dict(events=events, years=years, - year=year), + year=year, + timeframe=timeframe), context_instance=RequestContext(request)) diff --git a/ietf/templates/iesg/review_decisions.html b/ietf/templates/iesg/review_decisions.html index 24e1bc90e..e67f966b9 100644 --- a/ietf/templates/iesg/review_decisions.html +++ b/ietf/templates/iesg/review_decisions.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}IESG Review Decisions{% endblock %} +{% block title %}IESG Review Decisions in {{ timeframe }}{% endblock %} {% block morecss %} div.decisions h3 { margin-bottom: 0.5em; } @@ -8,9 +8,9 @@ div.decisions div { padding-left: 1em; text-indent: -1em; } {% endblock %} {% block content %} -

      IESG Review Decisions

      +

      IESG Review Decisions in {{ timeframe }}

      -

      Showing review decisions announced in {% if year %}{{ year }}{% else %}the past 6 months{% endif %}.

      +

      Showing review decisions announced in {{ timeframe }}.

      Announcements in: {% for y in years %}{{ y }}{% if not forloop.last %}, {% endif %}{% endfor %} @@ -19,7 +19,7 @@ div.decisions div { padding-left: 1em; text-indent: -1em; }

      {% for e in events %} {% ifchanged %}

      {{ e.time|date:"F j, Y" }}

      {% endifchanged %} -
      {{ e.desc }} {{ e.doc.name }} {% if e.doc.intended_std_level %}({{ e.doc.intended_std_level }}){% endif %} - {{ e.doc.title }}
      +
      {{ e.desc }}: {{ e.doc.name }} {% if e.doc.intended_std_level %}({{ e.doc.intended_std_level }}){% endif %} - {{ e.doc.title }}
      {% endfor %}
      From 11f3efdec7aff14c4e02f9dfdce135f986961fc8 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 7 Oct 2013 17:56:29 +0000 Subject: [PATCH 058/173] Add test of ReviewDecisions - Legacy-Id: 6380 --- ietf/iesg/tests.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 2eaec76f3..d68ddc1ae 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -7,12 +7,28 @@ from django.conf import settings from pyquery import PyQuery from ietf.utils.test_data import make_test_data -from ietf.doc.models import Document, TelechatDocEvent, State +from ietf.doc.models import Document, DocEvent, TelechatDocEvent, State from ietf.person.models import Person from ietf.group.models import Group from ietf.iesg.models import * from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized +class ReviewDecisionsTests(django.test.TestCase): + def test_review_decisions(self): + draft = make_test_data() + + e = DocEvent(type="iesg_approved") + e.doc = draft + e.by = Person.objects.get(name="Aread Irector") + e.save() + + url = urlreverse('ietf.iesg.views.review_decisions') + + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(draft.name in r.content) + + class IESGAgendaTests(django.test.TestCase): def test_feed(self): draft = make_test_data() From 53ec62a5c98b91d84fd749d430680d4ef971f9fd Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 9 Oct 2013 12:16:14 +0000 Subject: [PATCH 059/173] Fix a bug in the Secretariat telechat menu, "232" -> "s232" - Legacy-Id: 6394 --- ietf/secr/templates/telechat/base_telechat.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/secr/templates/telechat/base_telechat.html b/ietf/secr/templates/telechat/base_telechat.html index cecad39b8..02990e27e 100644 --- a/ietf/secr/templates/telechat/base_telechat.html +++ b/ietf/secr/templates/telechat/base_telechat.html @@ -38,7 +38,7 @@
    • 2.3.1 New Item
    • {% with agenda.docs.s231 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
    • 2.3.2 Returning Item
    • - {% with agenda.docs.232 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} + {% with agenda.docs.s232 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
    • 3 Document Actions
    • 3.1 WG Submissions
    • From bd34270cbc5df1c75ca40bb452ebdbe6f65aee57 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 9 Oct 2013 12:40:42 +0000 Subject: [PATCH 060/173] Port IESG agenda pages to new schema, add tests for them, move agenda utilities to agenda.py, fix a couple of things, remove obsolete test versions of agenda view, add support for viewing future IESG agendas through /agenda/YYYY-MM-DD/* (useful when testing) - Legacy-Id: 6395 --- ietf/iesg/agenda.py | 170 ++++++++ ietf/iesg/tests.py | 116 +++++- ietf/iesg/urls.py | 24 +- ietf/iesg/views.py | 385 +++++------------- ietf/secr/telechat/views.py | 40 +- .../secr/templates/telechat/doc_template.html | 2 +- ietf/templates/iesg/agenda.html | 8 +- ietf/templates/iesg/agenda_conflict_doc.html | 32 +- ietf/templates/iesg/agenda_conflict_doc.txt | 14 +- ietf/templates/iesg/agenda_doc.html | 42 +- ietf/templates/iesg/agenda_doc.txt | 18 +- ietf/templates/iesg/agenda_package.txt | 2 +- ietf/templates/iesg/agenda_wg.html | 10 +- ietf/templates/iesg/agenda_wg.txt | 4 +- .../iesg/moderator_conflict_doc.html | 26 +- ietf/templates/iesg/moderator_doc.html | 24 +- ietf/templates/iesg/moderator_package.html | 2 +- ietf/templates/iesg/moderator_wg.html | 6 +- ietf/templates/iesg/scribe_conflict_doc.html | 30 +- ietf/templates/iesg/scribe_doc.html | 26 +- ietf/templates/iesg/scribe_doc2.html | 12 +- 21 files changed, 523 insertions(+), 470 deletions(-) create mode 100644 ietf/iesg/agenda.py diff --git a/ietf/iesg/agenda.py b/ietf/iesg/agenda.py new file mode 100644 index 000000000..3d6d8dea6 --- /dev/null +++ b/ietf/iesg/agenda.py @@ -0,0 +1,170 @@ +# utilities for constructing agendas for IESG telechats + +import codecs, re, os, datetime + +from django.http import Http404 +from django.conf import settings + +from ietf.iesg.models import TelechatDate, TelechatAgendaItem +from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent +from ietf.group.models import Group, GroupMilestone + +def get_agenda_date(date=None): + if not date: + try: + return TelechatDate.objects.active().order_by('date')[0].date + except IndexError: + return datetime.date.today() + else: + try: + return TelechatDate.objects.active().get(date=datetime.datetime.strptime(date, "%Y-%m-%d").date()).date + except (ValueError, TelechatDate.DoesNotExist): + raise Http404 + +def get_doc_section(doc): + if doc.type_id == 'draft': + if doc.intended_std_level_id in ["bcp", "ds", "ps", "std"]: + s = "2" + else: + s = "3" + + g = doc.group_acronym() + if g and str(g) != 'none': + s = s + "1" + elif (s == "3") and doc.stream_id in ("ise","irtf"): + s = s + "3" + else: + s = s + "2" + if doc.get_state_slug() != "rfc" and doc.get_state_slug('draft-iesg') not in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"): + s = s + "3" + elif doc.returning_item(): + s = s + "2" + else: + s = s + "1" + elif doc.type_id == 'charter': + s = get_wg_section(doc.group) + elif doc.type_id == 'statchg': + protocol_action = False + for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')): + if relation.relationship.slug in ('tops','tois') or relation.target.document.std_level.slug in ('std','ds','ps'): + protocol_action = True + if protocol_action: + s="23" + else: + s="33" + if doc.get_state_slug() not in ("iesgeval", "defer", "appr-pr", "appr-pend", "appr-sent"): + s = s + "3" + elif doc.returning_item(): + s = s + "2" + else: + s = s + "1" + elif doc.type_id == 'conflrev': + if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'): + s = "343" + elif doc.returning_item(): + s = "342" + else: + s = "341" + + return s + +def get_wg_section(wg): + s = "" + charter_slug = None + if wg.charter: + charter_slug = wg.charter.get_state_slug() + if wg.state_id in ['active','dormant']: + if charter_slug in ['extrev','iesgrev']: + s = '422' + else: + s = '421' + else: + if charter_slug in ['extrev','iesgrev']: + s = '412' + else: + s = '411' + return s + +def agenda_docs(date): + matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).select_related("stream").distinct() + + docmatches = [] + + for doc in matches: + if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: + continue + + e = doc.latest_event(type="started_iesg_process") + doc.balloting_started = e.time if e else datetime.datetime.min + + if doc.type_id == "draft": + s = doc.get_state("draft-iana-review") + if s: # and s.slug in ("not-ok", "changed", "need-rev"): + doc.iana_review_state = str(s) + + if doc.get_state_slug("draft-iesg") == "lc": + e = doc.latest_event(LastCallDocEvent, type="sent_last_call") + if e: + doc.lastcall_expires = e.expires + + if doc.stream_id in ("ietf", "irtf", "iab"): + doc.consensus = "Unknown" + e = doc.latest_event(ConsensusDocEvent, type="changed_consensus") + if e: + doc.consensus = "Yes" if e.consensus else "No" + elif doc.type_id=='conflrev': + doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document + + docmatches.append(doc) + + # Be careful to keep this the same as what's used in agenda_documents + docmatches.sort(key=lambda d: d.balloting_started) + + res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4)) + for k in range(1,4): + res['s34%d'%k]=[] + for doc in docmatches: + section_key = "s" + get_doc_section(doc) + if section_key not in res: + res[section_key] = [] + res[section_key].append(doc) + return res + +def agenda_wg_actions(date): + res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4)) + charters = Document.objects.filter(type="charter", docevent__telechatdocevent__telechat_date=date).select_related("group").distinct() + charters = charters.filter(group__state__slug__in=["proposed","active"]) + for c in charters: + if c.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: + continue + + c.group.txt_link = settings.CHARTER_TXT_URL + "%s-%s.txt" % (c.canonical_name(), c.rev) + + section_key = "s" + get_wg_section(c.group) + if section_key not in res: + res[section_key] = [] + res[section_key].append(c) + return res + +def agenda_management_issues(date): + return TelechatAgendaItem.objects.filter(type=3).order_by('id') + +def agenda_data(request, date=None): + """Return a dict with the different IESG telechat agenda components.""" + date = get_agenda_date(date) + docs = agenda_docs(date) + mgmt = agenda_management_issues(date) + wgs = agenda_wg_actions(date) + data = {'date':str(date), 'docs':docs,'mgmt':mgmt,'wgs':wgs} + for key, filename in {'action_items':settings.IESG_TASK_FILE, + 'roll_call':settings.IESG_ROLL_CALL_FILE, + 'minutes':settings.IESG_MINUTES_FILE}.items(): + try: + f = codecs.open(filename, 'r', 'utf-8', 'replace') + text = f.read().strip() + f.close() + data[key] = text + except IOError: + data[key] = "(Error reading "+key+")" + return data + diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index d68ddc1ae..982fc7a87 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -1,4 +1,4 @@ -import os, shutil +import os, shutil, json import django.test from django.core.urlresolvers import reverse as urlreverse @@ -6,12 +6,14 @@ from django.conf import settings from pyquery import PyQuery +from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized from ietf.utils.test_data import make_test_data from ietf.doc.models import Document, DocEvent, TelechatDocEvent, State from ietf.person.models import Person from ietf.group.models import Group +from ietf.name.models import StreamName from ietf.iesg.models import * -from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized +from ietf.iesg.agenda import get_agenda_date class ReviewDecisionsTests(django.test.TestCase): def test_review_decisions(self): @@ -30,27 +32,113 @@ class ReviewDecisionsTests(django.test.TestCase): class IESGAgendaTests(django.test.TestCase): - def test_feed(self): - draft = make_test_data() + def setUp(self): + make_test_data() + ise_draft = Document.objects.get(name="draft-imaginary-independent-submission") + ise_draft.stream = StreamName.objects.get(slug="ise") + ise_draft.save() + + self.telechat_docs = { + "ietf_draft": Document.objects.get(name="draft-ietf-mars-test"), + "ise_draft": ise_draft, + "conflrev": Document.objects.get(name="conflict-review-imaginary-irtf-submission"), + "statusch": Document.objects.get(name="status-change-imaginary-mid-review"), + "charter": Document.objects.filter(type="charter")[0], + } + + by = Person.objects.get(name="Aread Irector") + date = get_agenda_date() + + for d in self.telechat_docs.values(): + TelechatDocEvent.objects.create(type="scheduled_for_telechat", + doc=d, + by=by, + telechat_date=date, + returning_item=True) + + def test_feed(self): url = "/feed/iesg-agenda/" r = self.client.get(url) self.assertEquals(r.status_code, 200) - self.assertTrue(draft.name not in r.content) - # add to schedule - e = TelechatDocEvent(type="scheduled_for_telechat") - e.doc = draft - e.by = Person.objects.get(name="Aread Irector") - e.telechat_date = TelechatDate.objects.active()[0].date - e.returning_item = True - e.save() + for d in self.telechat_docs.values(): + self.assertTrue(d.name in r.content) + self.assertTrue(d.title in r.content) + def test_agenda_json(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda_json")) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + self.assertTrue(json.loads(r.content)) + + def test_agenda(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda")) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_txt(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda_txt")) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_scribe_template(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda_scribe_template")) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + continue # scribe template doesn't contain chartering info + + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_moderator_package(self): + url = urlreverse("ietf.iesg.views.agenda_moderator_package") + login_testing_unauthorized(self, "secretary", url) r = self.client.get(url) self.assertEquals(r.status_code, 200) - self.assertTrue(draft.name in r.content) - self.assertTrue(draft.title in r.content) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_package(self): + url = urlreverse("ietf.iesg.views.agenda_package") + login_testing_unauthorized(self, "secretary", url) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) class RescheduleOnAgendaTestCase(django.test.TestCase): def test_reschedule(self): diff --git a/ietf/iesg/urls.py b/ietf/iesg/urls.py index 21c0c4384..12f6be8ee 100644 --- a/ietf/iesg/urls.py +++ b/ietf/iesg/urls.py @@ -38,28 +38,22 @@ from ietf.iesg import views urlpatterns = patterns('', (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }), - (r'^decisions/(?:(?P[0-9]{4})/)?$', views.review_decisions), (r'^ann/(?:ind|new|prev)/$', 'django.views.generic.simple.redirect_to', { 'url': "/iesg/decisions/", 'permanent': True }), - (r'^agenda/$', views.agenda), - (r'^agenda/agenda.txt$', views.agenda_txt), - (r'^agenda/agenda.json$', views.agenda_json), - (r'^agenda/scribe_template.html$', views.agenda_scribe_template), - (r'^agenda/moderator_package.html$', views.agenda_moderator_package), - (r'^agenda/agenda_package.txt$', views.agenda_package), + + (r'^decisions/(?:(?P[0-9]{4})/)?$', views.review_decisions), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?$', views.agenda), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?agenda.txt$', views.agenda_txt), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?agenda.json$', views.agenda_json), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?scribe_template.html$', views.agenda_scribe_template), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?moderator_package.html$', views.agenda_moderator_package), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?agenda_package.txt$', views.agenda_package), (r'^agenda/documents.txt$', views.agenda_documents_txt), (r'^agenda/documents/$', views.agenda_documents), (r'^agenda/telechat-(?P\d+)-(?P\d+)-(?P\d+)-docs.tgz', views.telechat_docs_tarfile), (r'^discusses/$', views.discusses), - (r'^milestones', views.milestones_needing_review), + (r'^milestones$', views.milestones_needing_review), (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), url(r'^wgactions/$', views.working_group_actions, name="iesg_working_group_actions"), url(r'^wgactions/add/$', views.edit_working_group_action, { 'wga_id': None }, name="iesg_add_working_group_action"), url(r'^wgactions/(?P\d+)/$', views.edit_working_group_action, name="iesg_edit_working_group_action"), ) - -if settings.SERVER_MODE != 'production': - urlpatterns += patterns('', - (r'^agenda/(?P\d{4}-\d\d-\d\d)/$', views.agenda), - (r'^_test/moderator_package.html$', views.agenda_moderator_package_test), - (r'^_test/agenda_package.txt', views.agenda_package_test), - ) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 1d53736cb..19b56a681 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -36,7 +36,6 @@ import codecs, re, os, glob import datetime import tarfile -from django.views.generic.list_detail import object_list from django.views.generic.simple import direct_to_template from django.views.decorators.vary import vary_on_cookie from django.core.urlresolvers import reverse as urlreverse @@ -51,12 +50,16 @@ from ietf.idtracker.models import IDInternal, InternetDraft, AreaGroup, Position from ietf.iesg.models import TelechatDates, TelechatAgendaItem, WGAction from ietf.idrfc.idrfc_wrapper import IdWrapper, RfcWrapper -from ietf.doc.utils import update_telechat -from ietf.ietfauth.decorators import group_required, role_required -from ietf.ietfauth.utils import has_role + +from ietf.iesg.models import TelechatDate, TelechatAgendaItem from ietf.ipr.models import IprDocAlias from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent from ietf.group.models import Group, GroupMilestone +from ietf.person.models import Person + +from ietf.doc.utils import update_telechat +from ietf.ietfauth.utils import has_role, role_required +from ietf.iesg.agenda import get_agenda_date, agenda_data, agenda_docs, agenda_wg_actions, agenda_management_issues def review_decisions(request, year=None): events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved")) @@ -85,150 +88,8 @@ def review_decisions(request, year=None): timeframe=timeframe), context_instance=RequestContext(request)) - -def get_doc_section(id): - pass - -def get_doc_sectionREDESIGN(doc): - if doc.type_id == 'draft': - if doc.intended_std_level_id in ["bcp", "ds", "ps", "std"]: - s = "2" - else: - s = "3" - - g = doc.group_acronym() - if g and str(g) != 'none': - s = s + "1" - elif (s == "3") and doc.stream_id in ("ise","irtf"): - s = s + "3" - else: - s = s + "2" - if not doc.get_state_slug=="rfc" and doc.get_state_slug('draft-iesg') not in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"): - s = s + "3" - elif doc.returning_item(): - s = s + "2" - else: - s = s + "1" - elif doc.type_id == 'charter': - s = get_wg_section(doc.group) - elif doc.type_id == 'statchg': - protocol_action = False - for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')): - if relation.relationship.slug in ('tops','tois') or relation.target.document.std_level.slug in ('std','ds','ps'): - protocol_action = True - if protocol_action: - s="23" - else: - s="33" - if doc.get_state_slug() not in ("iesgeval", "defer", "appr-pr", "appr-pend", "appr-sent"): - s = s + "3" - elif doc.returning_item(): - s = s + "2" - else: - s = s + "1" - elif doc.type_id == 'conflrev': - if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'): - s = "343" - elif doc.returning_item(): - s = "342" - else: - s = "341" - - return s - -def get_wg_section(wg): - s = "" - charter_slug = None - if wg.charter: - charter_slug = wg.charter.get_state_slug() - if wg.state_id in ['active','dormant']: - if charter_slug in ['extrev','iesgrev']: - s = '422' - else: - s = '421' - else: - if charter_slug in ['extrev','iesgrev']: - s = '412' - else: - s = '411' - return s - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - get_doc_section = get_doc_sectionREDESIGN - -def agenda_docs(date, next_agenda): - matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).select_related("stream").distinct() - - docmatches = [] - - for doc in matches: - if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: - continue - - e = doc.latest_event(type="started_iesg_process") - doc.balloting_started = e.time if e else datetime.datetime.min - - if doc.type_id == "draft": - s = doc.get_state("draft-iana-review") - if s: # and s.slug in ("not-ok", "changed", "need-rev"): - doc.iana_review_state = str(s) - - if doc.get_state_slug("draft-iesg") == "lc": - e = doc.latest_event(LastCallDocEvent, type="sent_last_call") - if e: - doc.lastcall_expires = e.expires - - if doc.stream_id in ("ietf", "irtf", "iab"): - doc.consensus = "Unknown" - e = doc.latest_event(ConsensusDocEvent, type="changed_consensus") - if e: - doc.consensus = "Yes" if e.consensus else "No" - elif doc.type_id=='conflrev': - doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document - - docmatches.append(doc) - - # Be careful to keep this the same as what's used in agenda_documents - docmatches.sort(key=lambda d: d.balloting_started) - - res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4)) - for k in range(1,4): - res['s34%d'%k]=[] - for id in docmatches: - section_key = "s"+get_doc_section(id) - if section_key not in res: - res[section_key] = [] - res[section_key].append({'obj':id}) - return res - -def agenda_wg_actions(date): - res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4)) - charters = Document.objects.filter(type="charter", docevent__telechatdocevent__telechat_date=date).select_related("group").distinct() - charters = charters.filter(group__state__slug__in=["proposed","active"]) - for c in charters: - if c.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: - continue - - c.group.txt_link = settings.CHARTER_TXT_URL + "%s-%s.txt" % (c.canonical_name(), c.rev) - - section_key = "s" + get_wg_section(c.group) - if section_key not in res: - res[section_key] = [] - # Cleanup - Older view code wants obj, newer wants doc. Older code should be moved forward - res[section_key].append({'obj': c.group, 'doc': c}) - return res - -def agenda_management_issues(date): - return TelechatAgendaItem.objects.filter(type=3).order_by('id') - -def _agenda_json(request, date=None): - if not date: - date = TelechatDates.objects.all()[0].date1 - next_agenda = True - else: - y,m,d = date.split("-") - date = datetime.date(int(y), int(m), int(d)) - next_agenda = None +def agenda_json(request, date=None): + date = get_agenda_date(date) data = {'telechat-date':str(date), 'as-of':str(datetime.datetime.utcnow()), @@ -272,83 +133,88 @@ def _agenda_json(request, date=None): data['sections']['6'] = {'title':"Management Issues"} data['sections']['7'] = {'title':"Working Group News"} - docs = agenda_docs(date, next_agenda) + docs = agenda_docs(date) for section in docs.keys(): # in case the document is in a state that does not have an agenda section - if section != 's': - s = str(".".join(list(section)[1:])) - if s[0:1] == '4': - # ignore these; not sure why they are included by agenda_docs - pass + if section == 's': + continue + + s = str(".".join(list(section)[1:])) + if s[0:1] == '4': + # ignore these; not sure why they are included by agenda_docs + continue + + if not docs[section]: + continue + + # If needed, add a "For Action" section to agenda + if s[4:5] == '3': + data['sections'][s] = {'title':"For Action", 'docs':[]} + + for d in docs[section]: + docinfo = {'docname':d.canonical_name(), + 'title':d.title, + 'ad':d.ad.name if d.ad else None } + if d.note: + docinfo['note'] = d.note + defer = d.active_defer_event() + if defer: + docinfo['defer-by'] = defer.by.name + docinfo['defer-at'] = str(defer.time) + if d.type_id == "draft": + docinfo['rev'] = d.rev + docinfo['intended-std-level'] = str(d.intended_std_level) + if d.rfc_number(): + docinfo['rfc-number'] = d.rfc_number() + + iana_state = d.get_state("draft-iana-review") + if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"): + docinfo['iana-review-state'] = str(iana_state) + + if d.get_state_slug("draft-iesg") == "lc": + e = d.latest_event(LastCallDocEvent, type="sent_last_call") + if e: + docinfo['lastcall-expires'] = e.expires.strftime("%Y-%m-%d") + + docinfo['consensus'] = None + e = d.latest_event(ConsensusDocEvent, type="changed_consensus") + if e: + docinfo['consensus'] = e.consensus + elif d.type_id == 'conflrev': + docinfo['rev'] = d.rev + td = d.relateddocument_set.get(relationship__slug='conflrev').target.document + docinfo['target-docname'] = td.canonical_name() + docinfo['target-title'] = td.title + docinfo['target-rev'] = td.rev + docinfo['intended-std-level'] = str(td.intended_std_level) + docinfo['stream'] = str(td.stream) else: - if len(docs[section]) != 0: - # If needed, add a "For Action" section to agenda - if s[4:5] == '3': - data['sections'][s] = {'title':"For Action", 'docs':[]} - - for obj in docs[section]: - d = obj['obj'] - docinfo = {'docname':d.canonical_name(), - 'title':d.title, - 'ad':d.ad.name} - if d.note: - docinfo['note'] = d.note - defer = d.active_defer_event() - if defer: - docinfo['defer-by'] = defer.by.name - docinfo['defer-at'] = str(defer.time) - if d.type_id == "draft": - docinfo['rev'] = d.rev - docinfo['intended-std-level'] = str(d.intended_std_level) - if d.rfc_number(): - docinfo['rfc-number'] = d.rfc_number() - - iana_state = d.get_state("draft-iana-review") - if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"): - docinfo['iana-review-state'] = str(iana_state) - - if d.get_state_slug("draft-iesg") == "lc": - e = d.latest_event(LastCallDocEvent, type="sent_last_call") - if e: - docinfo['lastcall-expires'] = e.expires.strftime("%Y-%m-%d") - - docinfo['consensus'] = None - e = d.latest_event(ConsensusDocEvent, type="changed_consensus") - if e: - docinfo['consensus'] = e.consensus - elif d.type_id == 'conflrev': - docinfo['rev'] = d.rev - td = d.relateddocument_set.get(relationship__slug='conflrev').target.document - docinfo['target-docname'] = td.canonical_name() - docinfo['target-title'] = td.title - docinfo['target-rev'] = td.rev - docinfo['intended-std-level'] = str(td.intended_std_level) - docinfo['stream'] = str(td.stream) - else: - # XXX check this -- is there nothing to set for - # all other documents here? - pass - data['sections'][s]['docs'] += [docinfo, ] + # XXX check this -- is there nothing to set for + # all other documents here? + pass + data['sections'][s]['docs'] += [docinfo, ] wgs = agenda_wg_actions(date) for section in wgs.keys(): # in case the charter is in a state that does not have an agenda section - if section != 's': - s = str(".".join(list(section)[1:])) - if s[0:1] != '4': - # ignore these; not sure why they are included by agenda_wg_actions - pass - else: - if len(wgs[section]) != 0: - for obj in wgs[section]: - wg = obj['obj'] - doc = obj['doc'] - wginfo = {'docname': doc.canonical_name(), - 'rev': doc.rev, - 'wgname': doc.group.name, - 'acronym': doc.group.acronym, - 'ad': doc.group.ad.name} - data['sections'][s]['wgs'] += [wginfo, ] + if section == 's': + continue + + s = str(".".join(list(section)[1:])) + if s[0:1] != '4': + # ignore these; not sure why they are included by agenda_wg_actions + continue + + if not wgs[section]: + continue + + for doc in wgs[section]: + wginfo = {'docname': doc.canonical_name(), + 'rev': doc.rev, + 'wgname': doc.group.name, + 'acronym': doc.group.acronym, + 'ad': doc.group.ad.name} + data['sections'][s]['wgs'] += [wginfo, ] mgmt = agenda_management_issues(date) num = 0 @@ -356,83 +222,36 @@ def _agenda_json(request, date=None): num += 1 data['sections']["6.%d" % num] = {'title':m.title} - return data - -def _agenda_data(request, date=None): - if not date: - date = TelechatDates.objects.all()[0].date1 - next_agenda = True - else: - y,m,d = date.split("-") - date = datetime.date(int(y), int(m), int(d)) - next_agenda = None - #date = "2006-03-16" - docs = agenda_docs(date, next_agenda) - mgmt = agenda_management_issues(date) - wgs = agenda_wg_actions(date) - data = {'date':str(date), 'docs':docs,'mgmt':mgmt,'wgs':wgs} - for key, filename in {'action_items':settings.IESG_TASK_FILE, - 'roll_call':settings.IESG_ROLL_CALL_FILE, - 'minutes':settings.IESG_MINUTES_FILE}.items(): - try: - f = codecs.open(filename, 'r', 'utf-8', 'replace') - text = f.read().strip() - f.close() - data[key] = text - except IOError: - data[key] = "(Error reading "+key+")" - return data + return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain') @vary_on_cookie def agenda(request, date=None): - data = _agenda_data(request, date) + data = agenda_data(request, date) data['private'] = 'private' in request.REQUEST data['settings'] = settings return render_to_response("iesg/agenda.html", data, context_instance=RequestContext(request)) -def agenda_txt(request): - data = _agenda_data(request) +def agenda_txt(request, date=None): + data = agenda_data(request, date) return render_to_response("iesg/agenda.txt", data, context_instance=RequestContext(request), mimetype="text/plain") -def agenda_json(request): - response = HttpResponse(mimetype='text/plain') - response.write(json.dumps(_agenda_json(request), indent=2)) - return response +def agenda_scribe_template(request, date=None): + date = get_agenda_date(date) + docs = agenda_docs(date) + return render_to_response('iesg/scribe_template.html', { 'date':str(date), 'docs':docs }, context_instance=RequestContext(request) ) -def agenda_scribe_template(request): - date = TelechatDates.objects.all()[0].date1 - docs = agenda_docs(date, True) - return render_to_response('iesg/scribe_template.html', {'date':str(date), 'docs':docs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}, context_instance=RequestContext(request) ) - -def _agenda_moderator_package(request): - data = _agenda_data(request) - data['ad_names'] = [str(x) for x in IESGLogin.active_iesg()] - data['ad_names'].sort(key=lambda x: x.split(' ')[-1]) +@role_required('Area Director', 'Secretariat') +def agenda_moderator_package(request, date=None): + data = agenda_data(request, date) + data['ads'] = sorted(Person.objects.filter(role__name="ad", role__group__state="active"), + key=lambda p: p.name_parts()[3]) return render_to_response("iesg/moderator_package.html", data, context_instance=RequestContext(request)) -@group_required('Area_Director','Secretariat') -def agenda_moderator_package(request): - return _agenda_moderator_package(request) - -def agenda_moderator_package_test(request): - if request.META['REMOTE_ADDR'] == "127.0.0.1": - return _agenda_moderator_package(request) - else: - return HttpResponseForbidden() - -def _agenda_package(request): - data = _agenda_data(request) +@role_required('Area Director', 'Secretariat') +def agenda_package(request, date=None): + data = agenda_data(request) return render_to_response("iesg/agenda_package.txt", data, context_instance=RequestContext(request), mimetype='text/plain') -@group_required('Area_Director','Secretariat') -def agenda_package(request): - return _agenda_package(request) - -def agenda_package_test(request): - if request.META['REMOTE_ADDR'] == "127.0.0.1": - return _agenda_package(request) - else: - return HttpResponseForbidden() def agenda_documents_txt(request): dates = TelechatDates.objects.all()[0].dates() @@ -665,7 +484,7 @@ def get_possible_wg_actions(): return res -@group_required('Area_Director', 'Secretariat') +@role_required('Area Director', 'Secretariat') def working_group_actions(request): current_items = WGAction.objects.order_by('status_date').select_related() @@ -716,7 +535,7 @@ class EditWGActionForm(forms.ModelForm): self.fields['telechat_date'].choices = choices -@group_required('Secretariat') +@role_required('Secretariat') def edit_working_group_action(request, wga_id): if wga_id != None: wga = get_object_or_404(WGAction, pk=wga_id) diff --git a/ietf/secr/telechat/views.py b/ietf/secr/telechat/views.py index 05d4e79bc..d8f3b96d4 100644 --- a/ietf/secr/telechat/views.py +++ b/ietf/secr/telechat/views.py @@ -8,15 +8,14 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from ietf.doc.models import DocEvent, Document, BallotDocEvent, BallotPositionDocEvent, TelechatDocEvent, WriteupDocEvent, save_document_in_history -from ietf.doc.proxy import InternetDraft from ietf.doc.utils import get_document_content, log_state_changed from ietf.group.models import Group from ietf.name.models import BallotPositionName from ietf.person.models import Person from ietf.doc.lastcall import request_last_call from ietf.doc.mails import email_ad, email_state_changed -from ietf.iesg.models import TelechatDate, TelechatAgendaItem, WGAction -from ietf.iesg.views import _agenda_data +from ietf.iesg.models import TelechatDate, TelechatAgendaItem +from ietf.iesg.agenda import agenda_data from forms import * import os @@ -38,9 +37,7 @@ active_ballot_positions: takes one argument, doc. returns a dictionary with a k NOTE: this function has been deprecated as of Datatracker 4.34. Should now use methods on the Document. For example: doc.active_ballot().active_ad_positions() -_agenda_data: takes a request object and a date string in the format YYYY-MM-DD. - - 2012-07-28 this function was changed to return Document objects instead - of old InternetDraft wrappers +agenda_data: takes a request object and a date string in the format YYYY-MM-DD. ''' # ------------------------------------------------- @@ -55,7 +52,7 @@ def get_doc_list(agenda): for key in sorted(agenda['docs']): docs.extend(agenda['docs'][key]) - return [x['obj'] for x in docs] + return docs def get_doc_writeup(doc): ''' @@ -100,15 +97,12 @@ def get_section_header(file,agenda): h3b = {'1':'Proposed for IETF Review','2':'Proposed for Approval'} h3c = {'1':'Under Evaluation for IETF Review','2':'Proposed for Approval'} - # Robert updated _agenda_data to return Document objects instead of the ID wrapper - #doc = InternetDraft.objects.get(filename=file) doc = Document.objects.get(name=file) - test = {'obj':doc} for k,v in agenda['docs'].iteritems(): - if test in v: + if doc in v: section = k - count = '%s of %s' % (v.index(test) + 1, len(v)) + count = '%s of %s' % (v.index(doc) + 1, len(v)) break header = [ '%s %s' % (section[1], h1[section[1]]) ] @@ -135,7 +129,7 @@ def get_first_doc(agenda): ''' for k,v in sorted(agenda['docs'].iteritems()): if v: - return v[0]['obj'] + return v[0] return None @@ -144,7 +138,7 @@ def get_first_doc(agenda): # ------------------------------------------------- def bash(request, date): - agenda = _agenda_data(request, date=date) + agenda = agenda_data(request, date=date) return render_to_response('telechat/bash.html', { 'agenda': agenda, @@ -158,7 +152,7 @@ def doc(request, date): displays the message "No Documents" ''' - agenda = _agenda_data(request, date=date) + agenda = agenda_data(request, date=date) doc = get_first_doc(agenda) if doc: url = reverse('telechat_doc_detail', kwargs={'date':date,'name':doc.name}) @@ -212,7 +206,7 @@ def doc_detail(request, date, name): 'substate':tag} BallotFormset = formset_factory(BallotForm, extra=0) - agenda = _agenda_data(request, date=date) + agenda = agenda_data(request, date=date) header = get_section_header(name,agenda) if name else '' # nav button logic @@ -329,7 +323,7 @@ def doc_navigate(request, date, name, nav): The view retrieves the appropriate document and redirects to the doc view. ''' doc = get_object_or_404(Document, docalias__name=name) - agenda = _agenda_data(request, date=date) + agenda = agenda_data(request, date=date) target = name docs = get_doc_list(agenda) @@ -346,10 +340,6 @@ def doc_navigate(request, date, name, nav): def main(request): ''' The is the main view where the user selects an existing telechat or creates a new one. - - NOTES ON EXTERNAL HELPER FUNCTIONS: - _agenda_data(): returns dictionary of agenda sections - get_ballot(name): returns a BallotWrapper and RfcWrapper or IdWrapper ''' if request.method == 'POST': date=request.POST['date'] @@ -371,7 +361,7 @@ def management(request, date): This view displays management issues and lets the user update the status ''' - agenda = _agenda_data(request, date=date) + agenda = agenda_data(request, date=date) issues = TelechatAgendaItem.objects.filter(type=3).order_by('id') return render_to_response('telechat/management.html', { @@ -396,7 +386,7 @@ def minutes(request, date): pa_docs = [ d for d in docs if d.intended_std_level.slug not in ('inf','exp','hist') ] da_docs = [ d for d in docs if d.intended_std_level.slug in ('inf','exp','hist') ] - agenda = _agenda_data(request, date=date) + agenda = agenda_data(request, date=date) return render_to_response('telechat/minutes.html', { 'agenda': agenda, @@ -422,8 +412,8 @@ def new(request): def roll_call(request, date): - agenda = _agenda_data(request, date=date) - ads = Person.objects.filter(role__name='ad') + agenda = agenda_data(request, date=date) + ads = Person.objects.filter(role__name='ad', role__group__state="active") sorted_ads = sorted(ads, key = lambda a: a.name_parts()[3]) return render_to_response('telechat/roll_call.html', { diff --git a/ietf/secr/templates/telechat/doc_template.html b/ietf/secr/templates/telechat/doc_template.html index 700a717c1..acb165d5a 100644 --- a/ietf/secr/templates/telechat/doc_template.html +++ b/ietf/secr/templates/telechat/doc_template.html @@ -1,7 +1,7 @@ {% if section_docs %} {% else %} diff --git a/ietf/templates/iesg/agenda.html b/ietf/templates/iesg/agenda.html index c83339a50..684c8f25d 100644 --- a/ietf/templates/iesg/agenda.html +++ b/ietf/templates/iesg/agenda.html @@ -65,11 +65,11 @@ div.agenda-wg { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width

      1. Administrivia

      -

      1.1 {% if private or user|in_group:"Area Director,IAB Chair,Secretariat" %}Roll Call{%else%}Roll Call{%endif%}

      +

      1.1 {% if private or user|has_role:"Area Director,IAB Chair,Secretariat" %}Roll Call{% else %}Roll Call{% endif %}

      1.2 Bash the Agenda

      -

      1.3 Approval of the {% if private or user|in_group:"Area Director,IAB Chair,Secretariat" %}Minutes{%else%}Minutes{%endif%} of Past Telechats

      +

      1.3 Approval of the {% if private or user|has_role:"Area Director,IAB Chair,Secretariat" %}Minutes{%else%}Minutes{%endif%} of Past Telechats

      1.4 List of Remaining Action Items from Last Telechat

       {{ action_items }}
      @@ -96,11 +96,11 @@ div.agenda-wg { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width
       
      {% for m in mgmt %}

      6.{{forloop.counter}} {{m.title|escape}}

      -{% if user|in_group:"Area Director,IAB Chair,Secretariat" %} +{% if user|has_role:"Area Director,IAB Chair,Secretariat" %}
       {{m.text|wordwrap:"76"}}
       
      -{% endif %}{# if user|in_group #} +{% endif %}{# if user|has_role #} {% endfor %}
      diff --git a/ietf/templates/iesg/agenda_conflict_doc.html b/ietf/templates/iesg/agenda_conflict_doc.html index 5e5692404..7d92fee97 100644 --- a/ietf/templates/iesg/agenda_conflict_doc.html +++ b/ietf/templates/iesg/agenda_conflict_doc.html @@ -68,29 +68,26 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endif %}

      {{ title3 }}

      {% for doc in section_docs %} -{% if forloop.first %} -{% endif %} -
      -{{doc.obj.name}}-{{doc.obj.rev}} -[txt] +{{doc.name}}-{{doc.rev}} +[txt] -
      {{ doc.obj.title|escape }} +
      {{ doc.title|escape }}
      -{{doc.obj.conflictdoc.name}}-{{doc.obj.conflictdoc.rev}} -[txt] -
      {{ doc.obj.conflictdoc.title|escape }} ({{doc.obj.conflictdoc.stream}}: {{ doc.obj.conflictdoc.intended_std_level }}) -{% if doc.obj.conflictdoc.note %} -
      Note: {{ doc.obj.conflictdoc.note|linebreaksbr }} +{{doc.conflictdoc.name}}-{{doc.conflictdoc.rev}} +[txt] +
      {{ doc.conflictdoc.title|escape }} ({{doc.conflictdoc.stream}}: {{ doc.conflictdoc.intended_std_level }}) +{% if doc.conflictdoc.note %} +
      Note: {{ doc.conflictdoc.note|linebreaksbr }} {% endif %} -{% if doc.obj.conflictdoc.ipr %} +{% if doc.conflictdoc.ipr %}
      IPR:
        - {% for ipr in doc.obj.conflictdoc.ipr %} + {% for ipr in doc.conflictdoc.ipr %} {% ifequal ipr.ipr.status 1 %}
      • {{ ipr.ipr.title|escape }}
      • {% endifequal %} @@ -100,17 +97,16 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endif %}
      -Token: {{ doc.obj.ad }} -{% with doc.obj.active_defer_event as defer %} +Token: {{ doc.ad }} +{% with doc.active_defer_event as defer %} {% if defer %}
      Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}} {% endif %} {% endwith %}
      -{% ballot_icon doc.obj %} +{% ballot_icon doc %}
      -{% if forloop.last %} -{% endif %} + {% empty %}

      NONE

      {% endfor %} diff --git a/ietf/templates/iesg/agenda_conflict_doc.txt b/ietf/templates/iesg/agenda_conflict_doc.txt index 3c7d920c2..e7363db6c 100644 --- a/ietf/templates/iesg/agenda_conflict_doc.txt +++ b/ietf/templates/iesg/agenda_conflict_doc.txt @@ -39,13 +39,13 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {{ title2 }} {% endif %}{{ title3 }} {% for doc in section_docs %} - o {{doc.obj.canonical_name}}-{{doc.obj.rev}} - {% filter wordwrap:"68"|indent|indent %}{{ doc.obj.title }}{% endfilter %} - {{doc.obj.conflictdoc.canonical_name}}-{{doc.obj.conflictdoc.rev}} - {% filter wordwrap:"66"|indent:"4" %}{{ doc.obj.conflictdoc.title }} ({{doc.obj.conflictdoc.stream}}: {{ doc.obj.conflictdoc.intended_std_level }}){% endfilter %} -{% if doc.obj.conflictdoc.note %}{# note: note is not escaped #} {% filter wordwrap:"64"|indent:"6" %}Note: {{ doc.obj.conflictdoc.note|striptags }}{% endfilter %} -{% endif %} Token: {{ doc.obj.ad }} -{% with doc.obj.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %} + o {{doc.canonical_name}}-{{doc.rev}} + {% filter wordwrap:"68"|indent|indent %}{{ doc.title }}{% endfilter %} + {{doc.conflictdoc.canonical_name}}-{{doc.conflictdoc.rev}} + {% filter wordwrap:"66"|indent:"4" %}{{ doc.conflictdoc.title }} ({{doc.conflictdoc.stream}}: {{ doc.conflictdoc.intended_std_level }}){% endfilter %} +{% if doc.conflictdoc.note %}{# note: note is not escaped #} {% filter wordwrap:"64"|indent:"6" %}Note: {{ doc.conflictdoc.note|striptags }}{% endfilter %} +{% endif %} Token: {{ doc.ad }} +{% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %} {% empty %} NONE {% endfor %} diff --git a/ietf/templates/iesg/agenda_doc.html b/ietf/templates/iesg/agenda_doc.html index 37544a7f2..9c7ae80ab 100644 --- a/ietf/templates/iesg/agenda_doc.html +++ b/ietf/templates/iesg/agenda_doc.html @@ -64,35 +64,32 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endif %}

      {{ title3 }}

      {% for doc in section_docs %} -{% if forloop.first %} -{% endif %} -
      -{{doc.obj.canonical_name}} -{% with doc.obj.rfc_number as rfc_number %} +{{doc.canonical_name}} +{% with doc.rfc_number as rfc_number %} {% if rfc_number %} [txt] {% else %} -[txt] +[txt] {% endif %} {% endwith %} -{% if doc.obj.stream %} - {{ doc.obj.stream }} stream{% endif %} +{% if doc.stream %} - {{ doc.stream }} stream{% endif %} -
      {{ doc.obj.title|escape }} ({{ doc.obj.intended_std_level }}) +
      {{ doc.title|escape }} ({{ doc.intended_std_level }}) -{% if doc.obj.note %} -
      Note: {{ doc.obj.note|linebreaksbr }} +{% if doc.note %} +
      Note: {{ doc.note|linebreaksbr }} {% endif %} -{% if doc.obj.ipr %} +{% if doc.ipr %}
      IPR:
        - {% for ipr in doc.obj.ipr %} + {% for ipr in doc.ipr %} {% ifequal ipr.ipr.status 1 %}
      • {{ ipr.ipr.title|escape }}
      • {% endifequal %} @@ -101,30 +98,29 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endif %} -
        Token: {{ doc.obj.ad }} ({{doc.obj.area_acronym}} area) -{% with doc.obj.active_defer_event as defer %} +
        Token: {{ doc.ad }} ({{doc.area_acronym}} area) +{% with doc.active_defer_event as defer %} {% if defer %}
        Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}} {% endif %} {% endwith %} -{% if doc.obj.iana_review_state %} -
        IANA Review: {{ doc.obj.iana_review_state }} +{% if doc.iana_review_state %} +
        IANA Review: {{ doc.iana_review_state }} {% endif %} -{% if doc.obj.consensus %} -
        Consensus: {{ doc.obj.consensus }} +{% if doc.consensus %} +
        Consensus: {{ doc.consensus }} {% endif %} -{% if doc.obj.lastcall_expires %} -
        Last call expires: {{ doc.obj.lastcall_expires|date:"Y-m-d" }} +{% if doc.lastcall_expires %} +
        Last call expires: {{ doc.lastcall_expires|date:"Y-m-d" }} {% endif %}
      -{% ballot_icon doc.obj %} +{% ballot_icon doc %}
      -{% if forloop.last %} -{% endif %} + {% empty %}

      NONE

      {% endfor %} diff --git a/ietf/templates/iesg/agenda_doc.txt b/ietf/templates/iesg/agenda_doc.txt index abb6706db..e6537c9dd 100644 --- a/ietf/templates/iesg/agenda_doc.txt +++ b/ietf/templates/iesg/agenda_doc.txt @@ -38,15 +38,15 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %} {{ title2 }} {% endif %}{{ title3 }} -{% for doc in section_docs %}{% with doc.obj.rfc_number as rfc_number %} - o {{doc.obj.canonical_name}}{% if not rfc_number %}-{{doc.obj.rev}}{% endif %}{% endwith %}{% if doc.obj.stream %} - {{ doc.obj.stream }} stream{% endif %} - {% filter wordwrap:"68"|indent|indent %}{{ doc.obj.title }} ({{ doc.obj.intended_std_level }}){% endfilter %} -{% if doc.obj.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.obj.note|striptags }}{% endfilter %} -{% endif %} Token: {{ doc.obj.ad }}{% if doc.obj.iana_review_state %} - IANA Review: {{ doc.obj.iana_review_state }}{% endif %}{% if doc.obj.consensus %} - Consensus: {{ doc.obj.consensus }}{% endif %}{% if doc.obj.lastcall_expires %} - Last call expires: {{ doc.obj.lastcall_expires|date:"Y-m-d" }}{% endif %} -{% with doc.obj.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %} +{% for doc in section_docs %}{% with doc.rfc_number as rfc_number %} + o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %} + {% filter wordwrap:"68"|indent|indent %}{{ doc.title }} ({{ doc.intended_std_level }}){% endfilter %} +{% if doc.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.note|striptags }}{% endfilter %} +{% endif %} Token: {{ doc.ad }}{% if doc.iana_review_state %} + IANA Review: {{ doc.iana_review_state }}{% endif %}{% if doc.consensus %} + Consensus: {{ doc.consensus }}{% endif %}{% if doc.lastcall_expires %} + Last call expires: {{ doc.lastcall_expires|date:"Y-m-d" }}{% endif %} +{% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %} {% empty %} NONE {% endfor %} diff --git a/ietf/templates/iesg/agenda_package.txt b/ietf/templates/iesg/agenda_package.txt index b06dee3c9..dcf8073c6 100644 --- a/ietf/templates/iesg/agenda_package.txt +++ b/ietf/templates/iesg/agenda_package.txt @@ -5,7 +5,7 @@ Contents: 1. Roll Call and Dial-In Instructions https://www.ietf.org/iesg/internal/rollcall.txt 2. Agenda - http://datatracker.ietf.org/iesg/agenda/?private + https://datatracker.ietf.org/iesg/agenda/ 3. Management Item Details https://datatracker.ietf.org/cgi-bin/display_news.cgi?template_type=3 4. Previous minutes diff --git a/ietf/templates/iesg/agenda_wg.html b/ietf/templates/iesg/agenda_wg.html index a5a61dd2c..4494ff8e9 100644 --- a/ietf/templates/iesg/agenda_wg.html +++ b/ietf/templates/iesg/agenda_wg.html @@ -37,16 +37,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

      {{ title2 }}

      {% endif %}

      {{ title3 }}

      -{% for wg in section_wgs %} +{% for doc in section_wgs %}
      -{% ballot_icon wg.doc %} +{% ballot_icon doc %} - -
      {{ wg.doc.group.name|escape }} ({{wg.doc.group.acronym}})
      -
      Area: {{ wg.doc.group.parent.acronym|upper }} ({{ wg.doc.ad|default:"Sponsoring AD not assigned" }})
      + +
      {{ doc.group.name|escape }} ({{doc.group.acronym}})
      +
      Area: {{ doc.group.parent.acronym|upper }} ({{ doc.ad|default:"Sponsoring AD not assigned" }})
      diff --git a/ietf/templates/iesg/agenda_wg.txt b/ietf/templates/iesg/agenda_wg.txt index 3d444ec2d..c1b093afe 100644 --- a/ietf/templates/iesg/agenda_wg.txt +++ b/ietf/templates/iesg/agenda_wg.txt @@ -34,8 +34,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %} {{ title2 }} {% endif %}{{ title3 }} -{% for wg in section_wgs %} - o {{ wg.obj.name }} ({{wg.obj.acronym}}) +{% for doc in section_wgs %} + o {{ doc.group.name }} ({{ doc.group.acronym }}) {% empty %} NONE {% endfor %} diff --git a/ietf/templates/iesg/moderator_conflict_doc.html b/ietf/templates/iesg/moderator_conflict_doc.html index 676de9a9a..92daad515 100644 --- a/ietf/templates/iesg/moderator_conflict_doc.html +++ b/ietf/templates/iesg/moderator_conflict_doc.html @@ -40,23 +40,23 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {{ title2 }}
      {{ title3 }} ({{ forloop.counter }} of {{ section_docs|length }}) -
      {{doc.obj.name}}-{{doc.obj.rev}}
      -({{ doc.obj.title|escape }})
      +
      {{doc.name}}-{{doc.rev}}
      +({{ doc.title|escape }})
      -{{doc.obj.conflictdoc.name}}-{{doc.obj.conflictdoc.rev}}
      -({{ doc.obj.conflictdoc.title|escape }})
      -Intended status: {{ doc.obj.conflictdoc.intended_std_level }}
      +{{doc.conflictdoc.name}}-{{doc.conflictdoc.rev}}
      +({{ doc.conflictdoc.title|escape }})
      +Intended status: {{ doc.conflictdoc.intended_std_level }}
      -
      Token: {{ doc.obj.ad.plain_name|escape }}
      -{% if doc.obj.type.slug == "draft" %} -Last call ends: {{ doc.obj.most_recent_ietflc.expires.date|default:"(none)" }} -{% if doc.obj.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} +
      Token: {{ doc.ad.plain_name|escape }}
      +{% if doc.type.slug == "draft" %} +Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} +{% if doc.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} {% endif %}
      -{% if doc.obj.active_ballot %} +{% if doc.active_ballot %}
                               Yes  No-Objection  Discuss  Abstain  Recuse
      -{% for pos in doc.obj.active_ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
      +{% for pos in doc.active_ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
       {% endfor %}
       
      @@ -68,13 +68,13 @@ Last call ends: {{ doc.obj.most_recent_ietflc.expires.date|default:"(none)" }} {% endif %} {% if title3|startswith:"3.4.1" or title3|startswith:"3.4.2" %} -

      Does anyone have an objection to the this conflict review response being sent to the {{doc.obj.conflictdoc.stream}}?

      +

      Does anyone have an objection to the this conflict review response being sent to the {{doc.conflictdoc.stream}}?

      {% endif %} {% if title3|startswith:"3.4.3" %}

      Who will do the review of this document?

      {% endif %} -

      Current State: {{ doc.obj.friendly_state }}
      +

      Current State: {{ doc.friendly_state }}
      Next State:
      Sub State:

      diff --git a/ietf/templates/iesg/moderator_doc.html b/ietf/templates/iesg/moderator_doc.html index ae5a4ea74..3e3171a40 100644 --- a/ietf/templates/iesg/moderator_doc.html +++ b/ietf/templates/iesg/moderator_doc.html @@ -40,19 +40,19 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {{ title2 }}
      {{ title3 }} ({{ forloop.counter }} of {{ section_docs|length }}) -

      {{doc.obj.name}}-{{doc.obj.rev}}
      -({{ doc.obj.title|escape }})
      -Intended status: {{ doc.obj.intended_std_level }}
      -Token: {{ doc.obj.ad.plain_name|escape }}
      -{% if doc.obj.type.slug == "draft" %} -Last call ends: {{ doc.obj.most_recent_ietflc.expires.date|default:"(none)" }} -{% if doc.obj.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} +

      {{doc.name}}-{{doc.rev}}
      +({{ doc.title|escape }})
      +Intended status: {{ doc.intended_std_level }}
      +Token: {{ doc.ad.plain_name|escape }}
      +{% if doc.type.slug == "draft" %} +Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} +{% if doc.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} {% endif %}

      -{% if doc.obj.active_ballot %} +{% if doc.active_ballot %}
                               Yes  No-Objection  Discuss  Abstain  Recuse
      -{% for pos in doc.obj.active_ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
      +{% for pos in doc.active_ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
       {% endfor %}
       
      @@ -69,16 +69,16 @@ Last call ends: {{ doc.obj.most_recent_ietflc.expires.date|default:"(none)" }} {% endif %} {% if title2|startswith:"3.1" or title2|startswith:"3.2" %} -

      Does anyone have an[y further] objection to this document being published as an {{ doc.obj.intended_std_level }} RFC?

      +

      Does anyone have an[y further] objection to this document being published as an {{ doc.intended_std_level }} RFC?

      {% endif %} {% if title3|startswith:"3.3.1" or title3|startswith:"3.3.2" %} -

      Does anyone have an objection to this conflict review response being sent to the {{doc.obj.conflictdoc.stream}}?

      +

      Does anyone have an objection to this conflict review response being sent to the {{doc.conflictdoc.stream}}?

      {% endif %} {% if title3|startswith:"3.3.3" %}

      Who will do the review of this document?

      {% endif %} -

      Current State: {{ doc.obj.friendly_state }}
      +

      Current State: {{ doc.friendly_state }}
      Next State:
      Sub State:

      diff --git a/ietf/templates/iesg/moderator_package.html b/ietf/templates/iesg/moderator_package.html index 8c8eb11d5..23de0b3cb 100644 --- a/ietf/templates/iesg/moderator_package.html +++ b/ietf/templates/iesg/moderator_package.html @@ -144,7 +144,7 @@ teleconference. The Secretariat will post them in the public archive.

      7. Working Group News

      {% filter linebreaks_crlf %}
      -{% for name in ad_names %}[ ] {{ name }}
      +{% for ad in ads %}[ ] {{ ad.plain_name }}
       {% endfor %}
       
      {% endfilter %} diff --git a/ietf/templates/iesg/moderator_wg.html b/ietf/templates/iesg/moderator_wg.html index 36c02c0ea..f12a76e92 100644 --- a/ietf/templates/iesg/moderator_wg.html +++ b/ietf/templates/iesg/moderator_wg.html @@ -31,12 +31,12 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% endcomment %}{% load ietf_filters %} -{% for wg in section_wgs %} +{% for doc in section_wgs %}

      {{ title1 }}
      {{ title2 }}
      -{{ title3 }} ({{ forloop.counter }} of {{ section_wgs|length }})

      +{{ title3 }} ({{ forloop.counter }} of {{ section_wgs|length }}) -

      {{ wg.obj.name }} ({{wg.obj.acronym}})
      +

      {{ doc.group.name }} ({{ doc.group.acronym }})
      {% if title3|startswith:"4.1.1" %}

      Does anyone have an objection to the charter being sent for diff --git a/ietf/templates/iesg/scribe_conflict_doc.html b/ietf/templates/iesg/scribe_conflict_doc.html index 8c618f9aa..7d1a9eaad 100644 --- a/ietf/templates/iesg/scribe_conflict_doc.html +++ b/ietf/templates/iesg/scribe_conflict_doc.html @@ -44,37 +44,37 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.

        {% for doc in section_docs %}
      • - {{ doc.obj.title|escape }} -
        {{doc.obj.canonical_name}} - [txt] + {{ doc.title|escape }} +
        {{doc.canonical_name}} + [txt]
        - {{ doc.obj.conflictdoc.title|escape }} ({{doc.obj.conflictdoc.stream}}: {{ doc.obj.conflictdoc.intended_std_level }}) -
        {{doc.obj.conflictdoc.canonical_name}} - [txt] - {% if doc.obj.conflictdoc.note %} -
        Note: {{ doc.obj.conflictdoc.note|linebreaksbr }} + {{ doc.conflictdoc.title|escape }} ({{doc.conflictdoc.stream}}: {{ doc.conflictdoc.intended_std_level }}) +
        {{doc.conflictdoc.canonical_name}} + [txt] + {% if doc.conflictdoc.note %} +
        Note: {{ doc.conflictdoc.note|linebreaksbr }} {% endif %} - {% for ipr in doc.obj.conflictdoc.ipr %} + {% for ipr in doc.conflictdoc.ipr %} {% ifequal ipr.ipr.status 1 %}
        IPR: {{ ipr.ipr.title|escape }} {% endifequal %} {% endfor %}
        - Token: {{ doc.obj.ad.plain_name|escape }} - {% if doc.obj.active_ballot %} -
        Discusses/comments [ballot]: + Token: {{ doc.ad.plain_name|escape }} + {% if doc.active_ballot %} +
        Discusses/comments [ballot]:
          - {% for p in doc.obj.active_ballot.active_ad_positions.values %} + {% for p in doc.active_ballot.active_ad_positions.values %} {% if p.pos %} {% if p.discuss %}
        • - {{ p.ad }}: Discuss [{{ p.discuss_time }}]: + {{ p.ad }}: Discuss [{{ p.discuss_time }}]:
          {{ p.discuss }}
        • {% endif %} {% if p.comment %}
        • - {{ p.ad }}: Comment [{{ p.comment_time }}]: + {{ p.ad }}: Comment [{{ p.comment_time }}]:
          {{ p.comment }}
        • {% endif %} diff --git a/ietf/templates/iesg/scribe_doc.html b/ietf/templates/iesg/scribe_doc.html index 22491e7ea..cf2fa54bf 100644 --- a/ietf/templates/iesg/scribe_doc.html +++ b/ietf/templates/iesg/scribe_doc.html @@ -44,38 +44,38 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
            {% for doc in section_docs %}
          • - {{ doc.obj.title|escape }} ({{ doc.obj.intended_std_level }}) -
            {{doc.obj.canonical_name}} - {% with doc.obj.rfc_number as rfc_number %} + {{ doc.title|escape }} ({{ doc.intended_std_level }}) +
            {{doc.canonical_name}} + {% with doc.rfc_number as rfc_number %} {% if rfc_number %} [txt] {% else %} - [txt] + [txt] {% endif %} {% endwith %} -
            Token: {{ doc.obj.ad.plain_name|escape }} ({{doc.obj.area_acronym}} area) - {% if doc.obj.note %} -
            Note: {{ doc.obj.note|linebreaksbr }} +
            Token: {{ doc.ad.plain_name|escape }} ({{doc.area_acronym}} area) + {% if doc.note %} +
            Note: {{ doc.note|linebreaksbr }} {% endif %} - {% for ipr in doc.obj.ipr %} + {% for ipr in doc.ipr %} {% ifequal ipr.ipr.status 1 %}
            IPR: {{ ipr.ipr.title|escape }} {% endifequal %} {% endfor %} - {% if doc.obj.active_ballot %} -
            Discusses/comments [ballot]: + {% if doc.active_ballot %} +
            Discusses/comments [ballot]:
              - {% for p in doc.obj.active_ballot.active_ad_positions.values %} + {% for p in doc.active_ballot.active_ad_positions.values %} {% if p.pos %} {% if p.discuss %}
            • - {{ p.ad }}: Discuss [{{ p.discuss_time }}]: + {{ p.ad }}: Discuss [{{ p.discuss_time }}]:
              {{ p.discuss|escape }}
            • {% endif %} {% if p.comment %}
            • - {{ p.ad }}: Comment [{{ p.comment_time }}]: + {{ p.ad }}: Comment [{{ p.comment_time }}]:
              {{ p.comment|escape }}
            • {% endif %} diff --git a/ietf/templates/iesg/scribe_doc2.html b/ietf/templates/iesg/scribe_doc2.html index a33ffd316..9297218a6 100644 --- a/ietf/templates/iesg/scribe_doc2.html +++ b/ietf/templates/iesg/scribe_doc2.html @@ -36,22 +36,22 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% load ietf_filters %} -

              {{ doc.obj.name }}

              +

              {{ doc.name }}

              {% autoescape off %} -{% if doc.obj.active_ballot %} +{% if doc.active_ballot %} {% endif%} -{% endautoescape %} \ No newline at end of file +{% endautoescape %} From 55c7c6967933a42330ec1661cfd89b43caa3697a Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 9 Oct 2013 12:41:59 +0000 Subject: [PATCH 061/173] Remove unused secr/ copy of agenda outline template (from iesg/) - Legacy-Id: 6396 --- .../templates/telechat/agenda_outline_23.html | 142 ------------------ 1 file changed, 142 deletions(-) delete mode 100644 ietf/secr/templates/telechat/agenda_outline_23.html diff --git a/ietf/secr/templates/telechat/agenda_outline_23.html b/ietf/secr/templates/telechat/agenda_outline_23.html deleted file mode 100644 index a3fc9c8d0..000000000 --- a/ietf/secr/templates/telechat/agenda_outline_23.html +++ /dev/null @@ -1,142 +0,0 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% with "2. Protocol Actions" as title1 %}{% with 1 as title1_first %} -{% with "2.1 WG Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "2.1.1 New Items" as title3 %} -{% with docs.s211 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "2.1.2 Returning Items" as title3 %} -{% with docs.s212 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s213 %} -{% with "2.1.3 For Action" as title3 %} -{% with docs.s213 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} -{% endwith %}{# title1_first #} - -{% with "2.2 Individual Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "2.2.1 New Items" as title3 %} -{% with docs.s221 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "2.2.2 Returning Items" as title3 %} -{% with docs.s222 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s223 %} -{% with "2.2.3 For Action" as title3 %} -{% with docs.s223 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %}{# title1 #} - -{% with "3. Document Actions" as title1 %}{% with 1 as title1_first %} - -{% with "3.1 WG Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "3.1.1 New Items" as title3 %} -{% with docs.s311 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.1.2 Returning Items" as title3 %} -{% with docs.s312 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s313 %} -{% with "3.1.3 For Action" as title3 %} -{% with docs.s313 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %}{# title1_first #} - -{% with "3.2 Individual Submissions Via AD" as title2 %} -{% with 1 as title2_first %} - -{% with "3.2.1 New Items" as title3 %} -{% with docs.s321 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.2.2 Returning Items" as title3 %} -{% with docs.s322 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s323 %} -{% with "3.2.3 For Action" as title3 %} -{% with docs.s323 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% with "3.3 IRTF and Independent Submission Stream Documents" as title2 %} -{% with 1 as title2_first %} - -{% with "3.3.1 New Items" as title3 %} -{% with docs.s331 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.3.2 Returning Items" as title3 %} -{% with docs.s332 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s333 %} -{% with "3.3.3 For Action" as title3 %} -{% with docs.s333 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %} From b99cf963aeae7a9475f8f29d3f5cfc4061a1ba03 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 9 Oct 2013 12:43:45 +0000 Subject: [PATCH 062/173] Move /iesg/milestones to /iesg/milestones/, apparently the URL (by accident) didn't include the ending slash - Legacy-Id: 6397 --- ietf/iesg/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/iesg/urls.py b/ietf/iesg/urls.py index 12f6be8ee..246588297 100644 --- a/ietf/iesg/urls.py +++ b/ietf/iesg/urls.py @@ -51,7 +51,7 @@ urlpatterns = patterns('', (r'^agenda/documents/$', views.agenda_documents), (r'^agenda/telechat-(?P\d+)-(?P\d+)-(?P\d+)-docs.tgz', views.telechat_docs_tarfile), (r'^discusses/$', views.discusses), - (r'^milestones$', views.milestones_needing_review), + (r'^milestones/$', views.milestones_needing_review), (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), url(r'^wgactions/$', views.working_group_actions, name="iesg_working_group_actions"), url(r'^wgactions/add/$', views.edit_working_group_action, { 'wga_id': None }, name="iesg_add_working_group_action"), From be9fd53d55cf0b0f016c4cf08e1bf212b7484d02 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 9 Oct 2013 12:53:13 +0000 Subject: [PATCH 063/173] Remove the ?private thing in /iesg/agenda/, the only use of it appears to be agenda_package.txt and that file already includes the links that are enabled by the flag - perhaps it predates the user modeling that allows ADs and Secretariat to see the links - Legacy-Id: 6398 --- ietf/iesg/views.py | 1 - ietf/templates/iesg/agenda.html | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 19b56a681..15e3acbba 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -227,7 +227,6 @@ def agenda_json(request, date=None): @vary_on_cookie def agenda(request, date=None): data = agenda_data(request, date) - data['private'] = 'private' in request.REQUEST data['settings'] = settings return render_to_response("iesg/agenda.html", data, context_instance=RequestContext(request)) diff --git a/ietf/templates/iesg/agenda.html b/ietf/templates/iesg/agenda.html index 684c8f25d..e8e0d7b2c 100644 --- a/ietf/templates/iesg/agenda.html +++ b/ietf/templates/iesg/agenda.html @@ -65,11 +65,11 @@ div.agenda-wg { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width

              1. Administrivia

              -

              1.1 {% if private or user|has_role:"Area Director,IAB Chair,Secretariat" %}Roll Call{% else %}Roll Call{% endif %}

              +

              1.1 {% if user|has_role:"Area Director,IAB Chair,Secretariat" %}Roll Call{% else %}Roll Call{% endif %}

              1.2 Bash the Agenda

              -

              1.3 Approval of the {% if private or user|has_role:"Area Director,IAB Chair,Secretariat" %}Minutes{%else%}Minutes{%endif%} of Past Telechats

              +

              1.3 Approval of the {% if user|has_role:"Area Director,IAB Chair,Secretariat" %}Minutes{%else%}Minutes{%endif%} of Past Telechats

              1.4 List of Remaining Action Items from Last Telechat

               {{ action_items }}
              
              From 53c59e56e36130e8ae481b2a436d9546f3f3be84 Mon Sep 17 00:00:00 2001
              From: Ole Laursen 
              Date: Wed, 9 Oct 2013 12:55:28 +0000
              Subject: [PATCH 064/173] Remove vary_on_cookie on agenda view, it's not needed
               anymore, Django already adds the vary: cookie header automatically  -
               Legacy-Id: 6399
              
              ---
               ietf/iesg/views.py | 2 --
               1 file changed, 2 deletions(-)
              
              diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
              index 15e3acbba..adfdee690 100644
              --- a/ietf/iesg/views.py
              +++ b/ietf/iesg/views.py
              @@ -37,7 +37,6 @@ import datetime
               import tarfile
               
               from django.views.generic.simple import direct_to_template
              -from django.views.decorators.vary import vary_on_cookie
               from django.core.urlresolvers import reverse as urlreverse
               from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseRedirect
               from django.template import RequestContext, Context, loader
              @@ -224,7 +223,6 @@ def agenda_json(request, date=None):
               
                   return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain')
               
              -@vary_on_cookie
               def agenda(request, date=None):
                   data = agenda_data(request, date)
                   data['settings'] = settings
              
              From 516a5fcc92f5251135fc3406583c8a7571a5ae24 Mon Sep 17 00:00:00 2001
              From: Ole Laursen 
              Date: Wed, 9 Oct 2013 15:55:45 +0000
              Subject: [PATCH 065/173] Port /iesg/agenda/documents.txt to new schema,
               compose the table in Python instead of using a template to make the code
               easier to read  - Legacy-Id: 6400
              
              ---
               ietf/iesg/views.py                       | 33 ++++++++++++++++-------
               ietf/templates/iesg/agenda_documents.txt | 34 ------------------------
               2 files changed, 24 insertions(+), 43 deletions(-)
               delete mode 100644 ietf/templates/iesg/agenda_documents.txt
              
              diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
              index adfdee690..ae2f2fc07 100644
              --- a/ietf/iesg/views.py
              +++ b/ietf/iesg/views.py
              @@ -251,16 +251,31 @@ def agenda_package(request, date=None):
               
               
               def agenda_documents_txt(request):
              -    dates = TelechatDates.objects.all()[0].dates()
              +    dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4])
              +
                   docs = []
              -    for date in dates:
              -        from ietf.doc.models import TelechatDocEvent
              -        for d in Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct():
              -            if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date:
              -                docs.append(d)
              -    t = loader.get_template('iesg/agenda_documents.txt')
              -    c = Context({'docs':docs,'special_stream_list':['ise','irtf']})
              -    return HttpResponse(t.render(c), mimetype='text/plain')
              +    for d in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).distinct():
              +        date = d.telechat_date()
              +        if date in dates:
              +            d.computed_telechat_date = date
              +            docs.append(d)
              +    docs.sort(key=lambda d: d.computed_telechat_date)
              +
              +    # output table
              +    rows = []
              +    rows.append("# Fields: telechat date, filename (draft-foo-bar or rfc1234), intended status, rfc editor submission flag (0=no, 1=yes), area acronym, AD name, version")
              +    for d in docs:
              +        row = (
              +            d.computed_telechat_date.isoformat(),
              +            d.name,
              +            unicode(d.intended_std_level),
              +            "1" if d.stream_id in ("ise", "irtf") else "0",
              +            unicode(d.area_acronym()).lower(),
              +            d.ad.plain_name() if d.ad else "None Assigned",
              +            d.rev,
              +            )
              +        rows.append("\t".join(row))
              +    return HttpResponse(u"\n".join(rows), mimetype='text/plain')
               
               class RescheduleForm(forms.Form):
                   telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False)
              diff --git a/ietf/templates/iesg/agenda_documents.txt b/ietf/templates/iesg/agenda_documents.txt
              deleted file mode 100644
              index 1079cd51d..000000000
              --- a/ietf/templates/iesg/agenda_documents.txt
              +++ /dev/null
              @@ -1,34 +0,0 @@
              -{% comment %}
              -Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
              -All rights reserved. Contact: Pasi Eronen  
              -
              -Redistribution and use in source and binary forms, with or without
              -modification, are permitted provided that the following conditions 
              -are met:
              -
              - * Redistributions of source code must retain the above copyright
              -   notice, this list of conditions and the following disclaimer.
              -
              - * Redistributions in binary form must reproduce the above
              -   copyright notice, this list of conditions and the following
              -   disclaimer in the documentation and/or other materials provided
              -   with the distribution.
              -
              - * Neither the name of the Nokia Corporation and/or its
              -   subsidiary(-ies) nor the names of its contributors may be used
              -   to endorse or promote products derived from this software
              -   without specific prior written permission.
              -
              -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
              -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
              -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
              -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
              -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
              -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
              -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
              -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
              -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
              -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
              -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
              -{% endcomment %}# Fields: telechat date, filename (draft-foo-bar or rfc1234), intended status, rfc editor submission flag (0=no, 1=yes), area acronym, AD name, version{% for doc in docs %}
              -{{ doc.telechat_date }}	{{ doc.name }}	{{ doc.intended_std_level }}	{% if doc.stream.slug in special_stream_list %}1{% else %}0{% endif %}	{{doc.area_acronym|lower}}	{% with doc.ad as ad %}{% if ad %}{{ ad.plain_name }}{% else %}None Assigned{% endif %}{% endwith %}	{{doc.rev}}{% endfor %}
              
              From bb952fa2815df4c27950b37d4658e935f70cda3d Mon Sep 17 00:00:00 2001
              From: Ole Laursen 
              Date: Wed, 9 Oct 2013 16:27:36 +0000
              Subject: [PATCH 066/173] Add test for agenda_documents_txt  - Legacy-Id: 6401
              
              ---
               ietf/iesg/tests.py | 9 +++++++++
               1 file changed, 9 insertions(+)
              
              diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py
              index 982fc7a87..3ac240fb4 100644
              --- a/ietf/iesg/tests.py
              +++ b/ietf/iesg/tests.py
              @@ -140,6 +140,15 @@ class IESGAgendaTests(django.test.TestCase):
                               self.assertTrue(d.name in r.content, "%s not in response" % k)
                               self.assertTrue(d.title in r.content, "%s title not in response" % k)
               
              +    def test_agenda_documents_txt(self):
              +        url = urlreverse("ietf.iesg.views.agenda_documents_txt")
              +        r = self.client.get(url)
              +        self.assertEquals(r.status_code, 200)
              +
              +        for k, d in self.telechat_docs.iteritems():
              +            self.assertTrue(d.name in r.content, "%s not in response" % k)
              +
              +
               class RescheduleOnAgendaTestCase(django.test.TestCase):
                   def test_reschedule(self):
                       draft = make_test_data()
              
              From a728468711933f0ae90cf8c354618e6da12f4617 Mon Sep 17 00:00:00 2001
              From: Ole Laursen 
              Date: Wed, 9 Oct 2013 16:28:36 +0000
              Subject: [PATCH 067/173] Fix some problems highlighted by the tests, making
               the code a bit more robust in case of unexpected data  - Legacy-Id: 6402
              
              ---
               ietf/doc/models.py | 2 +-
               ietf/iesg/views.py | 4 ++--
               2 files changed, 3 insertions(+), 3 deletions(-)
              
              diff --git a/ietf/doc/models.py b/ietf/doc/models.py
              index 7066f7443..8af021ee4 100644
              --- a/ietf/doc/models.py
              +++ b/ietf/doc/models.py
              @@ -306,7 +306,7 @@ class Document(DocumentInfo):
                       if g:
                           if g.type_id == "area":
                               return g.acronym
              -            elif g.type_id != "individ":
              +            elif g.type_id != "individ" and g.parent:
                               return g.parent.acronym
                       else:
                           return None
              diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
              index ae2f2fc07..bd7f25a01 100644
              --- a/ietf/iesg/views.py
              +++ b/ietf/iesg/views.py
              @@ -58,7 +58,7 @@ from ietf.person.models import Person
               
               from ietf.doc.utils import update_telechat
               from ietf.ietfauth.utils import has_role, role_required
              -from ietf.iesg.agenda import get_agenda_date, agenda_data, agenda_docs, agenda_wg_actions, agenda_management_issues
              +from ietf.iesg.agenda import *
               
               def review_decisions(request, year=None):
                   events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved"))
              @@ -212,7 +212,7 @@ def agenda_json(request, date=None):
                                     'rev': doc.rev,
                                     'wgname': doc.group.name,
                                     'acronym': doc.group.acronym,
              -                      'ad': doc.group.ad.name}
              +                      'ad': doc.group.ad.name if doc.group.ad else None}
                           data['sections'][s]['wgs'] += [wginfo, ]
               
                   mgmt = agenda_management_issues(date)
              
              From 0860342e34347b352d4e688f376b3e8343410e83 Mon Sep 17 00:00:00 2001
              From: Ole Laursen 
              Date: Thu, 10 Oct 2013 10:31:28 +0000
              Subject: [PATCH 068/173] Port /iesg/agenda/documents/ to new schema, fix
               problem with rows being colored for ADs with their position even if a ballot
               icon is not shown (issue #1128)  - Legacy-Id: 6405
              
              ---
               ietf/doc/models.py                            |  2 +-
               ietf/doc/templatetags/ballot_icon.py          | 54 +++++++++----
               ietf/iesg/views.py                            | 13 ++-
               ietf/templates/doc/search/status_columns.html |  9 +--
               ...ts_redesign.html => agenda_documents.html} | 80 +++++++++----------
               ...edesign.html => agenda_documents_row.html} | 43 +++++-----
               .../iesg/agenda_documents_row_status.html     |  4 +
               .../agenda_documents_row_status_redesign.html |  6 --
               8 files changed, 113 insertions(+), 98 deletions(-)
               rename ietf/templates/iesg/{agenda_documents_redesign.html => agenda_documents.html} (84%)
               rename ietf/templates/iesg/{agenda_documents_row_redesign.html => agenda_documents_row.html} (58%)
               create mode 100644 ietf/templates/iesg/agenda_documents_row_status.html
               delete mode 100644 ietf/templates/iesg/agenda_documents_row_status_redesign.html
              
              diff --git a/ietf/doc/models.py b/ietf/doc/models.py
              index 8af021ee4..0c700cb3f 100644
              --- a/ietf/doc/models.py
              +++ b/ietf/doc/models.py
              @@ -339,7 +339,7 @@ class Document(DocumentInfo):
                       return self.latest_event(LastCallDocEvent,type="sent_last_call")
               
                   def displayname_with_link(self):
              -        return '%s-%s' % (self.get_absolute_url(), self.name , self.rev)
              +        return mark_safe('%s-%s' % (self.get_absolute_url(), self.name , self.rev))
               
                   def rfc_number(self):
                       n = self.canonical_name()
              diff --git a/ietf/doc/templatetags/ballot_icon.py b/ietf/doc/templatetags/ballot_icon.py
              index 3604beaa8..ddcf01a9f 100644
              --- a/ietf/doc/templatetags/ballot_icon.py
              +++ b/ietf/doc/templatetags/ballot_icon.py
              @@ -40,11 +40,30 @@ from django.utils.safestring import mark_safe
               
               from ietf.ietfauth.utils import user_is_person, has_role
               from ietf.doc.models import BallotDocEvent, BallotPositionDocEvent, IESG_BALLOT_ACTIVE_STATES, IESG_SUBSTATE_TAGS
              +from ietf.name.models import BallotPositionName
               
               
               register = template.Library()
               
              -def render_ballot_icon(user, doc):
              +@register.filter
              +def showballoticon(doc):
              +    if doc.type_id == "draft":
              +        if doc.get_state_slug("draft-iesg") not in IESG_BALLOT_ACTIVE_STATES:
              +            return False
              +    elif doc.type_id == "charter":
              +        if doc.get_state_slug() not in ("intrev", "iesgrev"):
              +            return False
              +    elif doc.type_id == "conflrev":
              +       if doc.get_state_slug() not in ("iesgeval","defer"):
              +           return False
              +    elif doc.type_id == "statchg":
              +       if doc.get_state_slug() not in ("iesgeval","defer"):
              +           return False
              +
              +    return True
              +
              +
              +def render_ballot_icon(doc, user):
                   if not doc:
                       return ""
               
              @@ -53,18 +72,8 @@ def render_ballot_icon(user, doc):
                   if not isinstance(doc, Document):
                       doc = doc._draft
               
              -    if doc.type_id == "draft":
              -        if doc.get_state_slug("draft-iesg") not in IESG_BALLOT_ACTIVE_STATES:
              -            return ""
              -    elif doc.type_id == "charter":
              -        if doc.get_state_slug() not in ("intrev", "iesgrev"):
              -            return ""
              -    elif doc.type_id == "conflrev":
              -       if doc.get_state_slug() not in ("iesgeval","defer"):
              -           return ""
              -    elif doc.type_id == "statchg":
              -       if doc.get_state_slug() not in ("iesgeval","defer"):
              -           return ""
              +    if not showballoticon(doc):
              +        return ""
               
                   ballot = doc.active_ballot()
                   if not ballot:
              @@ -119,7 +128,7 @@ class BallotIconNode(template.Node):
                       self.doc_var = doc_var
                   def render(self, context):
                       doc = template.resolve_variable(self.doc_var, context)
              -        return render_ballot_icon(context.get("user"), doc)
              +        return render_ballot_icon(doc, context.get("user"))
               
               def do_ballot_icon(parser, token):
                   try:
              @@ -131,6 +140,23 @@ def do_ballot_icon(parser, token):
               register.tag('ballot_icon', do_ballot_icon)
               
               
              +@register.filter
              +def ballotposition(doc, user):
              +    if not showballoticon(doc) or not has_role(user, "Area Director"):
              +        return None
              +
              +    ballot = doc.active_ballot()
              +    if not ballot:
              +        return None
              +
              +    changed_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad__user=user, ballot=ballot)
              +    if changed_pos:
              +        pos = changed_pos.pos
              +    else:
              +        pos = BallotPositionName.objects.get(slug="norecord")
              +    return pos
              +
              +
               @register.filter
               def my_position(doc, user):
                   if not has_role(user, "Area Director"):
              diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
              index bd7f25a01..a396272fc 100644
              --- a/ietf/iesg/views.py
              +++ b/ietf/iesg/views.py
              @@ -306,8 +306,7 @@ def handle_reschedule_form(request, doc, dates):
                   if request.method == 'POST':
                       form = RescheduleForm(request.POST, **formargs)
                       if form.is_valid():
              -            login = request.user.get_profile()
              -            update_telechat(request, doc, login,
              +            update_telechat(request, doc, request.user.get_profile(),
                                           form.cleaned_data['telechat_date'],
                                           False if form.cleaned_data['clear_returning_item'] else None)
                           doc.time = datetime.datetime.now()
              @@ -319,21 +318,21 @@ def handle_reschedule_form(request, doc, dates):
                   return form
               
               def agenda_documents(request):
              -    dates = TelechatDates.objects.all()[0].dates()
              -    from ietf.doc.models import TelechatDocEvent
              +    dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4])
                   docs = []
              -    for d in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).distinct():
              +    for d in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).select_related().distinct():
                       if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date in dates:
                           docs.append(d)
               
                           e = d.latest_event(type="started_iesg_process")
                           d.balloting_started = e.time if e else datetime.datetime.min
                   docs.sort(key=lambda d: d.balloting_started)
              +
                   for i in docs:
                       i.reschedule_form = handle_reschedule_form(request, i, dates)
               
                   # some may have been taken off the schedule by the reschedule form
              -    docs = [d for d in docs if d.telechat_date()]
              +    docs = [d for d in docs if d.telechat_date() in dates]
               
                   telechats = []
                   for date in dates:
              @@ -351,7 +350,7 @@ def agenda_documents(request):
                               i.iprCount = len(i.ipr())
                           res[section_key].append(i)
                       telechats.append({'date':date, 'docs':res})
              -    return direct_to_template(request, 'iesg/agenda_documents_redesign.html', {'telechats':telechats, 'hide_telechat_date':True})
              +    return direct_to_template(request, 'iesg/agenda_documents.html', { 'telechats':telechats })
               
               def telechat_docs_tarfile(request,year,month,day):
                   from tempfile import mkstemp
              diff --git a/ietf/templates/doc/search/status_columns.html b/ietf/templates/doc/search/status_columns.html
              index 80691fbe5..71135cebf 100644
              --- a/ietf/templates/doc/search/status_columns.html
              +++ b/ietf/templates/doc/search/status_columns.html
              @@ -2,12 +2,9 @@
               
                 {{ doc.friendly_state|safe }} {% if not doc.get_state_slug == "rfc" %}{{ doc|state_age_colored }}{% endif %}
               
              -  {% if not hide_telechat_date and doc.telechat_date %}
              -    
              IESG Telechat: {{ doc.telechat_date }} - {% endif %} - - {% block extra_status %}{% endblock %} - + {% block extra_status %} + {% if doc.telechat_date %}
              IESG Telechat: {{ doc.telechat_date }}{% endif %} + {% endblock %} {% if doc.get_state_slug != "rfc" %}{# I-D #} diff --git a/ietf/templates/iesg/agenda_documents_redesign.html b/ietf/templates/iesg/agenda_documents.html similarity index 84% rename from ietf/templates/iesg/agenda_documents_redesign.html rename to ietf/templates/iesg/agenda_documents.html index e8a2affdc..e04827f12 100644 --- a/ietf/templates/iesg/agenda_documents_redesign.html +++ b/ietf/templates/iesg/agenda_documents.html @@ -39,22 +39,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% block morecss %} .agenda_docs tr.oddrow {background-color: #EDF5FF; } -.agenda_docs tr.header.telechat_date { margin-top:10px; background:#2647A0; color: white;} -.agenda_docs tr.header.telechat_date td { font-size: 125%; } +.agenda_docs tr.header.telechat-date { margin-top:10px; background:#2647A0; color: white;} +.agenda_docs tr.header.telechat-date td { font-size: 125%; } .agenda_docs tr.header + tr.header { border-top: 2px solid white;} -.agenda_docs tr td { - vertical-align: top; -} +.agenda_docs tr td { vertical-align: top; } .agenda_docs tr .reschedule, -.agenda_docs tr .clear-returning-item { - font-size: 11px; -} -.agenda_docs tr .doc_pages { -font-size:80%; font-style:italic; -} -.secretariat-actions { - margin-bottom: 10px; -} +.agenda_docs tr .clear-returning-item { font-size: 11px; } +.agenda_docs tr .doc_pages { font-size: 80%; font-style: italic; } +.secretariat-actions { margin-bottom: 1em; } {% endblock %} {% block pagehead %} @@ -65,12 +57,14 @@ font-size:80%; font-style:italic;

              Documents on Future IESG Telechat Agendas

              -{% if user|in_group:"Secretariat" %} + +{% if user|has_role:"Secretariat" %}
              - +
              {% endif %} - + + {% for t in telechats %} @@ -78,7 +72,7 @@ font-size:80%; font-style:italic; {% endif %} - + {% if forloop.first %} @@ -90,77 +84,77 @@ font-size:80%; font-style:italic; - {% for doc in t.docs.s211 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s211 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s212 %}{% endif %} - {% for doc in t.docs.s212 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s212 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s213 %}{% endif %} - {% for doc in t.docs.s213 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s213 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% for doc in t.docs.s221 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s221 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s222 %}{% endif %} - {% for doc in t.docs.s222 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s222 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s223 %}{% endif %} - {% for doc in t.docs.s223 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s223 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% for doc in t.docs.s231 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s231 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s222 %}{% endif %} - {% for doc in t.docs.s232 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s232 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s223 %}{% endif %} - {% for doc in t.docs.s233 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s233 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% for doc in t.docs.s311 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s311 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s312 %}{% endif %} - {% for doc in t.docs.s312 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s312 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s313 %}{% endif %} - {% for doc in t.docs.s313 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s313 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% for doc in t.docs.s321 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s321 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s322 %}{% endif %} - {% for doc in t.docs.s322 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s322 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s323 %}{% endif %} - {% for doc in t.docs.s323 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s323 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% for doc in t.docs.s331 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s331 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s332 %}{% endif %} - {% for doc in t.docs.s332 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s332 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s333 %}{% endif %} - {% for doc in t.docs.s333 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s333 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% for doc in t.docs.s341 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s341 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s342 %}{% endif %} - {% for doc in t.docs.s342 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s342 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s343 %}{% endif %} - {% for doc in t.docs.s343 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s343 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s411 or t.docs.s412%}{% endif %} {% if t.docs.s411 %}{% endif %} - {% for doc in t.docs.s411 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s411 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s412 %}{% endif %} - {% for doc in t.docs.s412 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s412 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s421 or t.docs.s422 %}{% endif %} {% if t.docs.s421 %}{% endif %} - {% for doc in t.docs.s421 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s421 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% if t.docs.s422 %}{% endif %} - {% for doc in t.docs.s422 %}{% include "iesg/agenda_documents_row_redesign.html" %}{%endfor%} + {% for doc in t.docs.s422 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} {% endfor %} diff --git a/ietf/templates/iesg/agenda_documents_row_redesign.html b/ietf/templates/iesg/agenda_documents_row.html similarity index 58% rename from ietf/templates/iesg/agenda_documents_row_redesign.html rename to ietf/templates/iesg/agenda_documents_row.html index 98f7878f1..fbbec4897 100644 --- a/ietf/templates/iesg/agenda_documents_row_redesign.html +++ b/ietf/templates/iesg/agenda_documents_row.html @@ -36,31 +36,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% load ballot_icon %} {% load ietf_filters %} - +{% endwith %} +{% endspaceless %} > -{% include "iesg/agenda_documents_row_status_redesign.html" %} +{% include "iesg/agenda_documents_row_status.html" %} {% include "doc/search/ipr_column_with_label.html" %} - + diff --git a/ietf/templates/iesg/agenda_documents_row_status.html b/ietf/templates/iesg/agenda_documents_row_status.html new file mode 100644 index 000000000..191d11647 --- /dev/null +++ b/ietf/templates/iesg/agenda_documents_row_status.html @@ -0,0 +1,4 @@ +{% extends "doc/search/status_columns.html" %} +{% block extra_status %} +{% if doc.type_id == 'draft' %}
              Intended status: {{ doc.intended_std_level }}{% endif %} +{% endblock %} diff --git a/ietf/templates/iesg/agenda_documents_row_status_redesign.html b/ietf/templates/iesg/agenda_documents_row_status_redesign.html deleted file mode 100644 index e73fa4d53..000000000 --- a/ietf/templates/iesg/agenda_documents_row_status_redesign.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "doc/search/status_columns.html" %} -{% block extra_status %} -{% if doc.type.slug == 'draft' %} -
              Intended status: {{ doc.intended_std_level }} -{% endif %} -{% endblock %} From a81af955000af865c5cf3f73e917de10fedf6a9b Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 10 Oct 2013 10:38:59 +0000 Subject: [PATCH 069/173] Add simple test for /iesg/agenda/documents/ - Legacy-Id: 6406 --- ietf/iesg/tests.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 3ac240fb4..a639b8f45 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -148,8 +148,17 @@ class IESGAgendaTests(django.test.TestCase): for k, d in self.telechat_docs.iteritems(): self.assertTrue(d.name in r.content, "%s not in response" % k) + def test_agenda_documents(self): + url = urlreverse("ietf.iesg.views.agenda_documents") + r = self.client.get(url) + self.assertEquals(r.status_code, 200) -class RescheduleOnAgendaTestCase(django.test.TestCase): + for k, d in self.telechat_docs.iteritems(): + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + +class RescheduleOnAgendaTests(django.test.TestCase): def test_reschedule(self): draft = make_test_data() From 21b32e72a36fb316e29d71d82156a2c2654e718c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 10 Oct 2013 11:12:13 +0000 Subject: [PATCH 070/173] Port /iesg/agenda/telechat-YYYY-MM-DD-docs.tgz, use tarfile.addfile and StringIO to avoid the temporary file - Legacy-Id: 6407 --- ietf/iesg/views.py | 54 +++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index a396272fc..4dd3b72b5 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -34,7 +34,7 @@ import codecs, re, os, glob import datetime -import tarfile +import tarfile, StringIO, time from django.views.generic.simple import direct_to_template from django.core.urlresolvers import reverse as urlreverse @@ -352,36 +352,40 @@ def agenda_documents(request): telechats.append({'date':date, 'docs':res}) return direct_to_template(request, 'iesg/agenda_documents.html', { 'telechats':telechats }) -def telechat_docs_tarfile(request,year,month,day): - from tempfile import mkstemp - date=datetime.date(int(year),int(month),int(day)) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import TelechatDocEvent - docs = [] - for d in IDInternal.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct(): - if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date: - docs.append(d) - else: - docs= IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1) +def telechat_docs_tarfile(request, date): + date = get_agenda_date(date) + + docs = [] + for d in Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct(): + if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date: + docs.append(d) + response = HttpResponse(mimetype='application/octet-stream') - response['Content-Disposition'] = 'attachment; filename=telechat-%s-%s-%s-docs.tgz'%(year, month, day) - tarstream = tarfile.open('','w:gz',response) - mfh, mfn = mkstemp() - manifest = open(mfn, "w") + response['Content-Disposition'] = 'attachment; filename=telechat-%s-docs.tgz' % date.isoformat() + + tarstream = tarfile.open('', 'w:gz', response) + + manifest = StringIO.StringIO() + for doc in docs: - doc_path = os.path.join(settings.INTERNET_DRAFT_PATH, doc.draft.filename+"-"+doc.draft.revision_display()+".txt") + doc_path = os.path.join(settings.INTERNET_DRAFT_PATH, doc.name + "-" + doc.rev + ".txt") if os.path.exists(doc_path): try: - tarstream.add(doc_path, str(doc.draft.filename+"-"+doc.draft.revision_display()+".txt")) - manifest.write("Included: "+doc_path+"\n") - except Exception, e: - manifest.write(("Failed (%s): "%e)+doc_path+"\n") + tarstream.add(doc_path, str(doc.name + "-" + doc.rev + ".txt")) + manifest.write("Included: %s\n" % doc_path) + except Exception as e: + manifest.write("Failed (%s): %s\n" % (e, doc_path)) else: - manifest.write("Not found: "+doc_path+"\n") - manifest.close() - tarstream.add(mfn, "manifest.txt") + manifest.write("Not found: %s\n" % doc_path) + + manifest.seek(0) + t = tarfile.TarInfo(name="manifest.txt") + t.size = len(manifest.buf) + t.mtime = time.time() + tarstream.addfile(t, manifest) + tarstream.close() - os.unlink(mfn) + return response def discusses(request): From 72cc9971514f60d275e90e93e4f54b701ae2e344 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 10 Oct 2013 15:53:24 +0000 Subject: [PATCH 071/173] Add test for /iesg/agenda/telechat-YYYY-MM-DD-docs.tgz - Legacy-Id: 6408 --- ietf/iesg/tests.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index a639b8f45..16ca09746 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -50,6 +50,10 @@ class IESGAgendaTests(django.test.TestCase): by = Person.objects.get(name="Aread Irector") date = get_agenda_date() + self.draft_dir = os.path.abspath("tmp-agenda-draft-dir") + os.mkdir(self.draft_dir) + settings.INTERNET_DRAFT_PATH = self.draft_dir + for d in self.telechat_docs.values(): TelechatDocEvent.objects.create(type="scheduled_for_telechat", doc=d, @@ -57,6 +61,10 @@ class IESGAgendaTests(django.test.TestCase): telechat_date=date, returning_item=True) + + def tearDown(self): + shutil.rmtree(self.draft_dir) + def test_feed(self): url = "/feed/iesg-agenda/" @@ -157,6 +165,35 @@ class IESGAgendaTests(django.test.TestCase): self.assertTrue(d.name in r.content, "%s not in response" % k) self.assertTrue(d.title in r.content, "%s title not in response" % k) + def test_agenda_telechat_docs(self): + d1 = self.telechat_docs["ietf_draft"] + d2 = self.telechat_docs["ise_draft"] + + d1_filename = "%s-%s.txt" % (d1.name, d1.rev) + d2_filename = "%s-%s.txt" % (d2.name, d2.rev) + + with open(os.path.join(self.draft_dir, d1_filename), "w") as f: + f.write("test content") + + url = urlreverse("ietf.iesg.views.telechat_docs_tarfile", kwargs=dict(date=get_agenda_date().isoformat())) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + + import tarfile, StringIO + + tar = tarfile.open(None, fileobj=StringIO.StringIO(r.content)) + names = tar.getnames() + self.assertTrue(d1_filename in names) + self.assertTrue(d2_filename not in names) + self.assertTrue("manifest.txt" in names) + + f = tar.extractfile(d1_filename) + self.assertEqual(f.read(), "test content") + + f = tar.extractfile("manifest.txt") + lines = list(f.readlines()) + self.assertTrue("Included" in [l for l in lines if d1_filename in l][0]) + self.assertTrue("Not found" in [l for l in lines if d2_filename in l][0]) class RescheduleOnAgendaTests(django.test.TestCase): def test_reschedule(self): From bfde86f52bf2a7f31a3b49ecc71e98fba92444bf Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 10 Oct 2013 15:54:32 +0000 Subject: [PATCH 072/173] Add support for status change in Document.get_file_path, ideally someone would clean up the directory structure and we would transition to something simpler - Legacy-Id: 6409 --- ietf/doc/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 0c700cb3f..ef0ae4507 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -80,6 +80,8 @@ class DocumentInfo(models.Model): return settings.CHARTER_PATH elif self.type_id == "conflrev": return settings.CONFLICT_REVIEW_PATH + elif self.type_id == "statchg": + return settings.STATUS_CHANGE_PATH else: raise NotImplemented From 77aa4723bfaa3e7a031acb1889d8f08d27084db2 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 10 Oct 2013 15:57:00 +0000 Subject: [PATCH 073/173] Use the right path for other document types in /iesg/agenda/telechat-YYYY-MM-DD-docs.tgz which now as a side-effect of the port can bundle more than just drafts - Legacy-Id: 6410 --- ietf/iesg/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 4dd3b72b5..b839cd5ba 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -368,7 +368,7 @@ def telechat_docs_tarfile(request, date): manifest = StringIO.StringIO() for doc in docs: - doc_path = os.path.join(settings.INTERNET_DRAFT_PATH, doc.name + "-" + doc.rev + ".txt") + doc_path = os.path.join(doc.get_file_path(), doc.name + "-" + doc.rev + ".txt") if os.path.exists(doc_path): try: tarstream.add(doc_path, str(doc.name + "-" + doc.rev + ".txt")) From f6ff2a80828fc6226b317c638040a181fbf21498 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 10 Oct 2013 16:12:34 +0000 Subject: [PATCH 074/173] Delete all old WGAction stuff, it's superceded by the WG charter support, clean up iesg/models.py a bit - Legacy-Id: 6411 --- ietf/iesg/admin.py | 4 - ietf/iesg/models.py | 123 ------------- ietf/iesg/tests.py | 130 -------------- ietf/iesg/urls.py | 8 +- ietf/iesg/views.py | 161 ------------------ ietf/secr/telechat/tests.py | 2 +- ietf/templates/base/left_menu.html | 4 - .../iesg/edit_working_group_action.html | 30 ---- .../templates/iesg/working_group_actions.html | 68 -------- ietf/utils/test_data.py | 11 +- 10 files changed, 5 insertions(+), 536 deletions(-) delete mode 100644 ietf/templates/iesg/edit_working_group_action.html delete mode 100644 ietf/templates/iesg/working_group_actions.html diff --git a/ietf/iesg/admin.py b/ietf/iesg/admin.py index 7f33b717e..8dfe86601 100644 --- a/ietf/iesg/admin.py +++ b/ietf/iesg/admin.py @@ -5,9 +5,5 @@ class TelechatAgendaItemAdmin(admin.ModelAdmin): pass admin.site.register(TelechatAgendaItem, TelechatAgendaItemAdmin) -class WGActionAdmin(admin.ModelAdmin): - pass -admin.site.register(WGAction, WGActionAdmin) - admin.site.register(TelechatDate) diff --git a/ietf/iesg/models.py b/ietf/iesg/models.py index f5b73b922..afed15b62 100644 --- a/ietf/iesg/models.py +++ b/ietf/iesg/models.py @@ -37,49 +37,6 @@ from django.conf import settings from ietf.idtracker.models import Acronym import datetime -# This table is not used by any code right now, and according to Glen, -# probably not currently (Aug 2009) maintained by the secretariat. -#class TelechatMinutes(models.Model): -# telechat_date = models.DateField(null=True, blank=True) -# telechat_minute = models.TextField(blank=True) -# exported = models.IntegerField(null=True, blank=True) -# def get_absolute_url(self): -# return "/iesg/telechat/%d/" % self.id -# def __str__(self): -# return "IESG Telechat Minutes for %s" % self.telechat_date -# class Meta: -# db_table = 'telechat_minutes' -# verbose_name = "Telechat Minute Text" -# verbose_name_plural = "Telechat Minutes" - -# this model is deprecated -class TelechatDates(models.Model): - date1 = models.DateField(primary_key=True, null=True, blank=True) - date2 = models.DateField(null=True, blank=True) - date3 = models.DateField(null=True, blank=True) - date4 = models.DateField(null=True, blank=True) - def dates(self): - l = [] - if self.date1: - l.append(self.date1) - if self.date2: - l.append(self.date2) - if self.date3: - l.append(self.date3) - if self.date4: - l.append(self.date4) - return l - - def save(self): - # date1 isn't really a primary id, so save() doesn't work - raise NotImplemented - - def __str__(self): - return " / ".join([str(d) for d in [self.date1,self.date2,self.date3,self.date4]]) - class Meta: - db_table = "telechat_dates" - verbose_name = "Next Telechat Date" - class TelechatAgendaItem(models.Model): TYPE_CHOICES = ( (1, "Working Group News"), @@ -98,34 +55,6 @@ class TelechatAgendaItem(models.Model): def __unicode__(self): type_name = self.TYPE_CHOICES_DICT.get(self.type, str(self.type)) return u'%s: %s' % (type_name, self.title or "") - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'templates' - -class WGAction(models.Model): - CATEGORY_CHOICES = ( - (11, "WG Creation::In Internal Review"), - (12, "WG Creation::Proposed for IETF Review"), - (13, "WG Creation::Proposed for Approval"), - (21, "WG Rechartering::In Internal Review"), - (22, "WG Rechartering::Under evaluation for IETF Review"), - (23, "WG Rechartering::Proposed for Approval") - ) - # note that with the new schema, Acronym is monkey-patched and is really Group - group_acronym = models.ForeignKey(Acronym, db_column='group_acronym_id', primary_key=True, unique=True) - note = models.TextField(blank=True,null=True) - status_date = models.DateField() - agenda = models.BooleanField("On Agenda") - token_name = models.CharField(max_length=25) - category = models.IntegerField(db_column='pwg_cat_id', choices=CATEGORY_CHOICES, default=11) - telechat_date = models.DateField() #choices = [(x.telechat_date,x.telechat_date) for x in Telechat.objects.all().order_by('-telechat_date')]) - def __str__(self): - return str(self.telechat_date)+": "+str(self.group_acronym) - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'group_internal' - ordering = ['-telechat_date'] - verbose_name = "WG Action" class Telechat(models.Model): telechat_id = models.IntegerField(primary_key=True) @@ -160,55 +89,3 @@ class TelechatDate(models.Model): class Meta: ordering = ['-date'] - -class TelechatDatesProxyDummy(object): - def all(self): - class Dummy(object): - def __getitem__(self, i): - return self - - def get_date(self, index): - if not hasattr(self, "date_cache"): - self.date_cache = TelechatDate.objects.active().order_by("date") - - if index < len(self.date_cache): - return self.date_cache[index].date - return None - - #date1 = models.DateField(primary_key=True, null=True, blank= True) - @property - def date1(self): - return self.get_date(0) - #date2 = models.DateField(null=True, blank=True) - @property - def date2(self): - return self.get_date(1) - #date3 = models.DateField(null=True, blank=True) - @property - def date3(self): - return self.get_date(2) - #date4 = models.DateField(null=True, blank=True) - @property - def date4(self): - return self.get_date(3) - - def dates(self): - l = [] - if self.date1: - l.append(self.date1) - if self.date2: - l.append(self.date2) - if self.date3: - l.append(self.date3) - if self.date4: - l.append(self.date4) - return l - - return Dummy() - -class TelechatDatesProxy(object): - objects = TelechatDatesProxyDummy() - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - TelechatDatesOld = TelechatDates - TelechatDates = TelechatDatesProxy diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 16ca09746..185218070 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -242,136 +242,6 @@ class RescheduleOnAgendaTests(django.test.TestCase): self.assertTrue(not draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").returning_item) self.assertEquals(draft.docevent_set.count(), events_before + 1) - -class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): - def setUp(self): - super(self.__class__, self).setUp() - - curdir = os.path.dirname(os.path.abspath(__file__)) - self.evaldir = os.path.join(curdir, "tmp-testdir") - os.mkdir(self.evaldir) - - src = os.path.join(curdir, "fixtures", "sieve-charter.txt") - shutil.copy(src, self.evaldir) - - settings.IESG_WG_EVALUATION_DIR = self.evaldir - - def tearDown(self): - super(self.__class__, self).tearDown() - shutil.rmtree(self.evaldir) - - - def test_working_group_actions(self): - make_test_data() - - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "secretary", url) - - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - for wga in WGAction.objects.all(): - self.assertTrue(wga.group_acronym.name in r.content) - - self.assertTrue('(sieve)' in r.content) - - def test_delete_wgaction(self): - make_test_data() - - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - login_testing_unauthorized(self, "secretary", url) - - r = self.client.post(url, dict(delete="1")) - self.assertEquals(r.status_code, 302) - self.assertTrue(not WGAction.objects.filter(pk=wga.pk)) - - def test_edit_wgaction(self): - make_test_data() - - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - login_testing_unauthorized(self, "secretary", url) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form select[name=token_name]')), 1) - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - # change - dates = TelechatDate.objects.active() - token_name = Person.objects.get(name="Ad No1").plain_name() - old = wga.pk - r = self.client.post(url, dict(status_date=dates[0].date.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates[3].date.isoformat())) - self.assertEquals(r.status_code, 302) - - wga = WGAction.objects.get(pk=old) - self.assertEquals(wga.status_date, dates[0].date) - self.assertEquals(wga.token_name, token_name) - self.assertEquals(wga.category, 23) - self.assertEquals(wga.note, "Testing.") - self.assertEquals(wga.telechat_date, dates[3].date) - - def test_add_possible_wg(self): - make_test_data() - - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "secretary", url) - - r = self.client.post(url, dict(add="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 302) - - # now we got back a URL we can use for adding, but first make - # sure we got a proposed group with the acronym - group = Group.objects.create( - name="Sieve test test", - acronym="sieve", - state_id="proposed", - type_id="wg", - parent=None - ) - - add_url = r['Location'] - r = self.client.get(add_url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue('(sieve)' in r.content) - self.assertEquals(len(q('form select[name=token_name]')), 1) - self.assertEquals(q('form input[name=status_date]')[0].get("value"), "2010-05-07") - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - wgas_before = WGAction.objects.all().count() - dates = TelechatDate.objects.active() - token_name = Person.objects.get(name="Ad No1").plain_name() - r = self.client.post(add_url, - dict(status_date=dates[0].date.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates[3].date.isoformat())) - self.assertEquals(r.status_code, 302) - self.assertEquals(wgas_before + 1, WGAction.objects.all().count()) - - def test_delete_possible_wg(self): - make_test_data() - - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "secretary", url) - - r = self.client.post(url, dict(delete="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 200) - - self.assertTrue('(sieve)' not in r.content) - - class IesgUrlTestCase(SimpleUrlTestCase): def testUrls(self): self.doTestUrls(__file__) diff --git a/ietf/iesg/urls.py b/ietf/iesg/urls.py index 246588297..9f0fc6c86 100644 --- a/ietf/iesg/urls.py +++ b/ietf/iesg/urls.py @@ -39,6 +39,7 @@ from ietf.iesg import views urlpatterns = patterns('', (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }), (r'^ann/(?:ind|new|prev)/$', 'django.views.generic.simple.redirect_to', { 'url': "/iesg/decisions/", 'permanent': True }), + (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), (r'^decisions/(?:(?P[0-9]{4})/)?$', views.review_decisions), (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?$', views.agenda), @@ -47,13 +48,10 @@ urlpatterns = patterns('', (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?scribe_template.html$', views.agenda_scribe_template), (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?moderator_package.html$', views.agenda_moderator_package), (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?agenda_package.txt$', views.agenda_package), + (r'^agenda/documents.txt$', views.agenda_documents_txt), (r'^agenda/documents/$', views.agenda_documents), - (r'^agenda/telechat-(?P\d+)-(?P\d+)-(?P\d+)-docs.tgz', views.telechat_docs_tarfile), + (r'^agenda/telechat-(?:(?P\d{4}-\d{2}-\d{2})-)?docs.tgz', views.telechat_docs_tarfile), (r'^discusses/$', views.discusses), (r'^milestones/$', views.milestones_needing_review), - (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), - url(r'^wgactions/$', views.working_group_actions, name="iesg_working_group_actions"), - url(r'^wgactions/add/$', views.edit_working_group_action, { 'wga_id': None }, name="iesg_add_working_group_action"), - url(r'^wgactions/(?P\d+)/$', views.edit_working_group_action, name="iesg_edit_working_group_action"), ) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index b839cd5ba..efe506dfc 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -46,8 +46,6 @@ from django.utils import simplejson as json from django import forms from ietf.idtracker.models import IDInternal, InternetDraft, AreaGroup, Position, IESGLogin, Acronym - -from ietf.iesg.models import TelechatDates, TelechatAgendaItem, WGAction from ietf.idrfc.idrfc_wrapper import IdWrapper, RfcWrapper from ietf.iesg.models import TelechatDate, TelechatAgendaItem @@ -436,162 +434,3 @@ def milestones_needing_review(request): ), context_instance=RequestContext(request)) -def parse_wg_action_file(path): - f = open(path, 'rU') - - line = f.readline() - while line and not line.strip(): - line = f.readline() - - # name - m = re.search(r'([^\(]*) \(', line) - if not m: - return None - name = m.group(1) - - # acronym - m = re.search(r'\((\w+)\)', line) - if not m: - return None - acronym = m.group(1) - - # date - line = f.readline() - m = re.search(r'(\d{4})-(\d{2})-(\d{2})', line) - while line and not m: - line = f.readline() - m = re.search(r'(\d{4})-(\d{2})-(\d{2})', line) - - last_updated = None - if m: - try: - last_updated = datetime.date(int(m.group(1)), int(m.group(2)), int(m.group(3))) - except: - pass - - # token - line = f.readline() - while line and not 'area director' in line.lower(): - line = f.readline() - - line = f.readline() - line = f.readline() - m = re.search(r'\s*(\w+)\s*', line) - token = "" - if m: - token = m.group(1) - - return dict(filename=os.path.basename(path), name=name, acronym=acronym, - status_date=last_updated, token=token) - -def get_possible_wg_actions(): - res = [] - charters = glob.glob(os.path.join(settings.IESG_WG_EVALUATION_DIR, '*-charter.txt')) - for path in charters: - d = parse_wg_action_file(path) - if d: - if not d['status_date']: - d['status_date'] = datetime.date(1900,1,1) - res.append(d) - - res.sort(key=lambda x: x['status_date']) - - return res - - -@role_required('Area Director', 'Secretariat') -def working_group_actions(request): - current_items = WGAction.objects.order_by('status_date').select_related() - - if request.method == 'POST' and has_role(request.user, 'Secretariat'): - filename = request.POST.get('filename') - if filename and filename in os.listdir(settings.IESG_WG_EVALUATION_DIR): - if 'delete' in request.POST: - os.unlink(os.path.join(settings.IESG_WG_EVALUATION_DIR, filename)) - if 'add' in request.POST: - d = parse_wg_action_file(os.path.join(settings.IESG_WG_EVALUATION_DIR, filename)) - qstr = "?" + "&".join("%s=%s" % t for t in d.iteritems()) - return HttpResponseRedirect(urlreverse('iesg_add_working_group_action') + qstr) - - - skip = [c.group_acronym.acronym for c in current_items] - possible_items = filter(lambda x: x['acronym'] not in skip, - get_possible_wg_actions()) - - return render_to_response("iesg/working_group_actions.html", - dict(current_items=current_items, - possible_items=possible_items), - context_instance=RequestContext(request)) - -class EditWGActionForm(forms.ModelForm): - token_name = forms.ChoiceField(required=True) - telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False) - - class Meta: - model = WGAction - fields = ['status_date', 'token_name', 'category', 'note'] - - def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - - # token name choices - self.fields['token_name'].choices = [("", "(None)")] + [(p.plain_name(), p.plain_name()) for p in IESGLogin.active_iesg().order_by('first_name')] - - # telechat choices - dates = TelechatDates.objects.all()[0].dates() - init = kwargs['initial']['telechat_date'] - if init and init not in dates: - dates.insert(0, init) - - choices = [("", "(not on agenda)")] - for d in dates: - choices.append((d, d.strftime("%Y-%m-%d"))) - - self.fields['telechat_date'].choices = choices - - -@role_required('Secretariat') -def edit_working_group_action(request, wga_id): - if wga_id != None: - wga = get_object_or_404(WGAction, pk=wga_id) - else: - wga = WGAction() - try: - wga.group_acronym = Acronym.objects.get(acronym=request.GET.get('acronym')) - except Acronym.DoesNotExist: - pass - - wga.token_name = request.GET.get('token') - try: - d = datetime.datetime.strptime(request.GET.get('status_date'), '%Y-%m-%d').date() - except: - d = datetime.date.today() - wga.status_date = d - wga.telechat_date = TelechatDates.objects.all()[0].date1 - wga.agenda = True - - initial = dict(telechat_date=wga.telechat_date if wga.agenda else None) - - if request.method == 'POST': - if "delete" in request.POST: - wga.delete() - return HttpResponseRedirect(urlreverse('iesg_working_group_actions')) - - form = EditWGActionForm(request.POST, instance=wga, initial=initial) - if form.is_valid(): - form.save(commit=False) - wga.agenda = bool(form.cleaned_data['telechat_date']) - if wga.category in (11, 21): - wga.agenda = False - if wga.agenda: - wga.telechat_date = form.cleaned_data['telechat_date'] - wga.save() - return HttpResponseRedirect(urlreverse('iesg_working_group_actions')) - else: - form = EditWGActionForm(instance=wga, initial=initial) - - - return render_to_response("iesg/edit_working_group_action.html", - dict(wga=wga, - form=form), - context_instance=RequestContext(request)) diff --git a/ietf/secr/telechat/tests.py b/ietf/secr/telechat/tests.py index dd60a68ef..39be67267 100644 --- a/ietf/secr/telechat/tests.py +++ b/ietf/secr/telechat/tests.py @@ -1,7 +1,7 @@ from django.core.urlresolvers import reverse from django.test import TestCase -from ietf.iesg.models import TelechatDate, TelechatAgendaItem, WGAction +from ietf.iesg.models import TelechatDate, TelechatAgendaItem from ietf.person.models import Person from ietf.utils.test_data import make_test_data diff --git a/ietf/templates/base/left_menu.html b/ietf/templates/base/left_menu.html index 2ee2545ce..9b44d11ad 100644 --- a/ietf/templates/base/left_menu.html +++ b/ietf/templates/base/left_menu.html @@ -45,16 +45,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
            • Next Telechat
            • Discusses
            • Milestones
            • - {# FIXME: this link should be removed when the old WG Actions are completely dead #} -
            • Working Groups
            • {% endif %} {% if user|has_role:"Secretariat" %}
            • Secretariat
            • Telechat Dates
            • Management Items
            • Milestones
            • - {# FIXME: this link should be removed when the old WG Actions are completely dead #} -
            • Working Groups
            • Sync discrepancies {% endif %} {% streams_menu %} diff --git a/ietf/templates/iesg/edit_working_group_action.html b/ietf/templates/iesg/edit_working_group_action.html deleted file mode 100644 index d126f631c..000000000 --- a/ietf/templates/iesg/edit_working_group_action.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Edit Working Group Action {{ wga.group_acronym.name }}{% endblock %} - -{% block morecss %} -form table th { - vertical-align: top; - text-align: left; - font-weight: normal; - padding-top: 3px; -} -{% endblock %} - -{% block content %} -{% load ietf_filters %} - -

              {{ wga.group_acronym.name }} ({{ wga.group_acronym.acronym }})

              - - -
            •  
              IESG telechat {{t.date}}
              IESG telechat {{t.date}}
              Full IESG Agenda
              2.1 WG Submissions
              2.1.2 Returning Item
              2.1.3 For Action
              2.2 Individual Submissions
              2.2.2 Returning Item
              2.2.3 For Action
              2.3 Status Changes
              2.3.2 Returning Item
              2.3.3 For Action
              3. Document Actions
              3.1 WG Submissions
              3.1.2 Returning Item
              3.1.3 For Action
              3.2 Individual Submissions Via AD
              3.2.2 Returning Item
              3.2.3 For Action
              3.3 Status Changes
              3.3.2 Returning Item
              3.3.3 For Action
              3.4 IRTF and Independent Submission Stream Documents
              3.4.2 Returning Item
              3.4.3 For Action
              4. Working Group Actions
              4.1 WG Creation
              4.1.1 Proposed for IETF Review
              4.1.2 Proposed for Approval
              4.2 WG Rechartering
              4.2.1 Under Evaluation for IETF Review
              4.2.2 Proposed for Approval
              -
              {{ doc.displayname_with_link|safe }}
              -{% with doc.active_defer_event as defer %}{% if defer %} -
              (deferred on {{ defer.time|date:"Y-m-d" }})
              -{% endif %}{% endwith %} -{% if user|in_group:"Secretariat" %} -
              -{% if doc.reschedule_form.show_clear %} -
              -{% endif %} -{% endif %} +
              {{ doc.displayname_with_link }}
              + {% with doc.active_defer_event as defer %}{% if defer %} +
              (deferred on {{ defer.time|date:"Y-m-d" }})
              + {% endif %}{% endwith %} + {% if user|has_role:"Secretariat" %} +
              + {% if doc.reschedule_form.show_clear %} +
              + {% endif %} + {% endif %}
              {{ doc.title }} -{% with doc.pages as pagecount %}{% if pagecount %}({{doc.pages}} pp){% endif %}{% endwith %} + {% if doc.pages %}({{doc.pages}} pp){% endif %} {{ doc.ad.plain_name|default:"" }}{{ doc.ad|default:"" }}
              - {{ form.as_table }} -
              - -
              - Back - -
              - -
              -{% endblock %} diff --git a/ietf/templates/iesg/working_group_actions.html b/ietf/templates/iesg/working_group_actions.html deleted file mode 100644 index 7ab3e7783..000000000 --- a/ietf/templates/iesg/working_group_actions.html +++ /dev/null @@ -1,68 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Working Group Actions{% endblock %} - -{% block morecss %} -.working-group-action .name, -.working-group-action .edit { - padding-left: 10px; -} -.working-group-action form input { - margin-left: 10px; -} -{% endblock %} - -{% block content %} -{% load ietf_filters %} -

              Working Group Actions

              - -

              These are the submitted WG descriptions and milestones for all -Proposed Working Groups and recharter submissions, along with -whatever status information is available.

              - -

              NOTE: Explicit direction by the AD is required to add the group to -an IESG Telechat agenda.

              - -

              Current Items

              - - -{% for wga in current_items %} - - - - {% if user|in_group:"Secretariat" %} - - {% endif %} - -{% endfor %} -
              {{ wga.status_date|date:"Y, M d" }}{{ wga.group_acronym.name }} ({{ wga.group_acronym.acronym }}) -
              - Edit - -
              -
              - -

              Possible Items

              - - -{% for wga in possible_items %} - - - - {% if user|in_group:"Secretariat" %} - - {% endif %} - -{% endfor %} -
              {{ wga.status_date|date:"Y, M d" }}{{ wga.name }} ({{ wga.acronym }}) -
              - - - -
              -
              -{% endblock %} - -{% block content_end %} - -{% endblock %} diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index 1e9ffc83c..e1de97d4a 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -1,7 +1,7 @@ from django.conf import settings from django.contrib.auth.models import User -from ietf.iesg.models import TelechatDate, WGAction +from ietf.iesg.models import TelechatDate from ietf.ipr.models import IprDetail, IprDocAlias from ietf.meeting.models import Meeting from ietf.doc.models import * @@ -98,15 +98,6 @@ def make_test_data(): ) group.charter = charter group.save() - WGAction.objects.create( - pk=group.pk, - note="", - status_date=datetime.date.today(), - agenda=1, - token_name="Aread", - category=13, - telechat_date=date2 - ) # persons From d11f2331fe91f4349a76dabf2486e3e328b8cca9 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:02:08 +0000 Subject: [PATCH 075/173] Remove invalid text-align: top from CSS - Legacy-Id: 6417 --- static/css/base2.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/base2.css b/static/css/base2.css index a53638b2a..30533c8d4 100644 --- a/static/css/base2.css +++ b/static/css/base2.css @@ -186,7 +186,7 @@ form table .help { .color2 { color: #00ffff; } .bgcolor1 { background-color: #ffb000; } .bgcolor2 { background-color: #00ffff; } -.square { width: 0.8ex; height: 0.8ex; margin: 0; padding: 0; display: inline-block; position: relative; top: 0.8ex; text-align: top;} +.square { width: 0.8ex; height: 0.8ex; margin: 0; padding: 0; display: inline-block; position: relative; top: 0.8ex; } .big { font-size: 109.5%; margin: 0; padding: 0; } .large { font-size: 120%; margin: 0; padding: 0; } .huge { font-size: 144%; margin: 0; padding: 0; } From 1975ff27d04d7fb36db2e350155d55414c264c55 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:03:40 +0000 Subject: [PATCH 076/173] Port IESG discusses to new schema - Legacy-Id: 6418 --- ietf/iesg/views.py | 56 ++++++++++-------- ietf/templates/iesg/discusses.html | 94 ++++++++++-------------------- static/js/iesg-discusses.js | 33 +++++++++++ 3 files changed, 95 insertions(+), 88 deletions(-) create mode 100644 static/js/iesg-discusses.js diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index efe506dfc..ac03e0b89 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -43,19 +43,17 @@ from django.template import RequestContext, Context, loader from django.shortcuts import render_to_response, get_object_or_404 from django.conf import settings from django.utils import simplejson as json +from django.db import models from django import forms -from ietf.idtracker.models import IDInternal, InternetDraft, AreaGroup, Position, IESGLogin, Acronym -from ietf.idrfc.idrfc_wrapper import IdWrapper, RfcWrapper - from ietf.iesg.models import TelechatDate, TelechatAgendaItem from ietf.ipr.models import IprDocAlias -from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent +from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent, IESG_BALLOT_ACTIVE_STATES from ietf.group.models import Group, GroupMilestone from ietf.person.models import Person -from ietf.doc.utils import update_telechat -from ietf.ietfauth.utils import has_role, role_required +from ietf.doc.utils import update_telechat, augment_events_with_revision +from ietf.ietfauth.utils import has_role, role_required, user_is_person from ietf.iesg.agenda import * def review_decisions(request, year=None): @@ -387,31 +385,39 @@ def telechat_docs_tarfile(request, date): return response def discusses(request): - res = [] + possible_docs = Document.objects.filter(models.Q(states__type="draft-iesg", + states__slug__in=IESG_BALLOT_ACTIVE_STATES) | + models.Q(states__type="charter", + states__slug__in=("intrev", "iesgrev")) | + models.Q(states__type__in=("statchg", "conflrev"), + states__slug__in=("iesgeval", "defer")), + docevent__ballotpositiondocevent__pos__blocking=True) + possible_docs = possible_docs.select_related("stream", "group", "ad").distinct()[:10] - for d in IDInternal.objects.filter(states__type="draft-iesg", states__slug__in=("pub-req", "ad-eval", "review-e", "lc-req", "lc", "writeupw", "goaheadw", "iesg-eva", "defer", "watching"), docevent__ballotpositiondocevent__pos="discuss").distinct(): - found = False - for p in d.positions.all(): - if p.discuss: - found = True - break - - if not found: + docs = [] + for doc in possible_docs: + ballot = doc.active_ballot() + if not ballot: continue - if d.rfc_flag: - doc = RfcWrapper(d) - else: - doc = IdWrapper(draft=d) + blocking_positions = [p for p in ballot.all_positions() if p.pos.blocking] - if doc.in_ietf_process() and doc.ietf_process.has_active_iesg_ballot(): - # quick hack - to be removed when the proxy code is removed - doc.underlying = doc.underlying_document() - doc.underlying.milestones = d.groupmilestone_set.filter(state="active").order_by("time").select_related("group") + if not blocking_positions: + continue - res.append(doc) + augment_events_with_revision(doc, blocking_positions) - return direct_to_template(request, 'iesg/discusses.html', {'docs':res}) + doc.by_me = bool([p for p in blocking_positions if user_is_person(request.user, p.ad)]) + doc.for_me = user_is_person(request.user, doc.ad) + doc.milestones = doc.groupmilestone_set.filter(state="active").order_by("time").select_related("group") + doc.blocking_positions = blocking_positions + + docs.append(doc) + + # latest first + docs.sort(key=lambda d: min(p.time for p in d.blocking_positions), reverse=True) + + return direct_to_template(request, 'iesg/discusses.html', { 'docs': docs }) @role_required('Area Director', 'Secretariat') def milestones_needing_review(request): diff --git a/ietf/templates/iesg/discusses.html b/ietf/templates/iesg/discusses.html index 6682b617e..ef134858a 100644 --- a/ietf/templates/iesg/discusses.html +++ b/ietf/templates/iesg/discusses.html @@ -39,81 +39,49 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% block title %}IESG Discuss Positions{% endblock %} {% block morecss %} -.discuss_hidden {display:none;} +.discusses-chooser label { display: inline-block; margin-right: 0.5em; } +.discusses-chooser input { vertical-align: middle; margin: 0; padding: 0; margin-right: 0.3em; } {% endblock %} {% block content %}

              IESG Discuss Positions

              -{% if user|in_group:"Area_Director" %} -
              -Show: All -By me -For me -
              +{% if user|has_role:"Area Director" %} +

              + Show: + + + +

              {% endif %} - -{% for doc in docs %} - - -{% with doc.underlying as doc %}{% include "doc/search/status_columns.html" %}{% endwith %} - - - -{% endfor %} + + + + + + + + {% for doc in docs %} + + + + {% include "doc/search/status_columns.html" %} + + + + + {% endfor %}
              DocumentStatusArea DirectorDiscusses
              {{ doc.displayname_with_link|safe }}{{ doc.ad_name|default:"" }} -{% for po in doc.ietf_process.iesg_ballot.get_discuss|dictsort:"is_old_ad" %} -{%if po.is_old_ad %}[{%endif%}{{po.ad_name}}{%if po.is_old_ad %}]{%endif%} ({% if po.discuss_date %}{{po.discuss_date|timesince_days}}{%endif%} days ago{% if doc.is_id_wrapper %}{% ifnotequal po.discuss_revision doc.latest_revision %} for -{{po.discuss_revision}}{% endifnotequal %}{% endif %})
              -{% endfor %} -
              DocumentStatusArea DirectorDiscusses
              {{ doc.displayname_with_link }}{{ doc.ad|default:"" }} + {% for p in doc.blocking_positions %} + {% if p.old_ad %}[{% endif %}{{ p.ad }}{% if p.old_ad %}]{% endif %} ({% if p.discuss_time %}{{ p.discuss_time|timesince_days }}{% endif %} days ago{% if doc.get_state_url != "rfc" and p.rev != doc.rev %} for -{{ p.rev }}{% endif %})
              + {% endfor %} +
              {% endblock content %} -{% block scripts %} -{% if user|in_group:"Area_Director" %} -function update_even_odd() { - var els = YAHOO.util.Dom.getElementsByClassName("discuss_row","tr"); - var j = 1; - for (var i = 0; i < els.length; i++) { - if (!YAHOO.util.Dom.hasClass(els[i], "discuss_hidden")) { - if ((j % 2) == 1) { - YAHOO.util.Dom.replaceClass(els[i], "evenrow", "oddrow"); - } else { - YAHOO.util.Dom.replaceClass(els[i], "oddrow", "evenrow"); - } - j++; - } - } -} -function radio_changed() { - var els1 = YAHOO.util.Dom.getElementsByClassName("discuss_notbyme","tr"); - YAHOO.util.Dom.removeClass(els1, "discuss_hidden"); - var els2 = YAHOO.util.Dom.getElementsByClassName("discuss_notforme","tr"); - YAHOO.util.Dom.removeClass(els2, "discuss_hidden"); - if (document.getElementById("discusses_byme").checked) { - YAHOO.util.Dom.addClass(els1, "discuss_hidden"); - location.hash = "#byme"; - } else if (document.getElementById("discusses_forme").checked) { - YAHOO.util.Dom.addClass(els2, "discuss_hidden"); - location.hash = "#forme"; - } else { - location.hash = "#"; - } - update_even_odd(); -} -var url = location.href.split('#'); -if (url[1] == 'byme') { - document.getElementById("discusses_byme").checked = true; - radio_changed(); -} else if (url[1] == 'forme') { - document.getElementById("discusses_forme").checked = true; - radio_changed(); -} -{% endif %}{# user in_group #} -{% endblock scripts %} - {% block js %} + {% endblock %} diff --git a/static/js/iesg-discusses.js b/static/js/iesg-discusses.js new file mode 100644 index 000000000..f3b98e151 --- /dev/null +++ b/static/js/iesg-discusses.js @@ -0,0 +1,33 @@ +jQuery(function () { + var radioButtons = jQuery('input[name="discusses"]'); + + var url = window.location.hash.replace("#", ""); + if (url == "byme" || url == "forme") + radioButtons.filter("[value=" + url + "]").click(); + + function updateDisplayedRows() { + var rows = jQuery(".discuss-row"); + + var val = radioButtons.filter(":checked").val(); + + if (val == "all") { + rows.show(); + if (window.location.hash) + window.location.hash = ""; + } + else if (val == "forme" || val == "byme") { + console.log(rows.filter("." + val)) + rows.filter("." + val).show(); + rows.not("." + val).hide(); + window.location.hash = val; + } + + // odd/even are swapped because the jQuery filter is 0-indexed + rows.filter(":visible").filter(":even").removeClass("even").addClass("odd"); + rows.filter(":visible").filter(":odd").removeClass("odd").addClass("even"); + } + + radioButtons.click(updateDisplayedRows); + + updateDisplayedRows(); +}); From dcfe368f745af505c24bc7b73d82713c1e79169f Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:05:33 +0000 Subject: [PATCH 077/173] Streamline a couple of doc helpers to prevent unnecessary DB lookups, speeds up IESG discusses page up a fair bit - Legacy-Id: 6419 --- ietf/doc/views_doc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 180a478ff..18f540591 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -59,12 +59,10 @@ def render_document_top(request, doc, tab, name): ballot = doc.latest_event(BallotDocEvent, type="created_ballot") if doc.type_id in ("draft","conflrev", "statchg"): - # if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot: tabs.append(("IESG Evaluation Record", "ballot", urlreverse("doc_ballot", kwargs=dict(name=name)), ballot, None if ballot else "IESG Evaluation Ballot has not been created yet")) elif doc.type_id == "charter": tabs.append(("IESG Review", "ballot", urlreverse("doc_ballot", kwargs=dict(name=name)), ballot, None if ballot else "IEST Review Ballot has not been created yet")) - # FIXME: if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot: if doc.type_id not in ["conflrev", "statchg"]: tabs.append(("IESG Writeups", "writeup", urlreverse("doc_writeup", kwargs=dict(name=name)), True)) From b6c4f725b9c10ff0932ecb2dcc07270b921c9f66 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:06:38 +0000 Subject: [PATCH 078/173] Fix accidental debug slicing in IESG discusses - of note is that the discusses page now supports more than just I-Ds - Legacy-Id: 6420 --- ietf/iesg/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index ac03e0b89..893371abb 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -392,7 +392,7 @@ def discusses(request): models.Q(states__type__in=("statchg", "conflrev"), states__slug__in=("iesgeval", "defer")), docevent__ballotpositiondocevent__pos__blocking=True) - possible_docs = possible_docs.select_related("stream", "group", "ad").distinct()[:10] + possible_docs = possible_docs.select_related("stream", "group", "ad").distinct() docs = [] for doc in possible_docs: From b63c6225645ce435cc2ec80e6e610ff7602425bb Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:07:16 +0000 Subject: [PATCH 079/173] Streamline a couple of doc helpers to prevent unnecessary DB lookups, speeds up IESG discusses page up a fair bit - Legacy-Id: 6421 --- ietf/doc/models.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index ef0ae4507..4cfef7eb4 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -122,7 +122,7 @@ class DocumentInfo(models.Model): if not hasattr(self, "state_cache") or self.state_cache == None: self.state_cache = {} - for s in self.states.all().select_related(): + for s in self.states.all().select_related("type"): self.state_cache[s.type_id] = s return self.state_cache.get(state_type, None) @@ -149,8 +149,8 @@ class DocumentInfo(models.Model): def active_ballot(self): """Returns the most recently created ballot if it isn't closed.""" - ballot = self.latest_event(BallotDocEvent, type="created_ballot") - if ballot and self.ballot_open(ballot.ballot_type.slug): + ballot = self.latest_event(BallotDocEvent, type__in=("created_ballot", "closed_ballot")) + if ballot and ballot.type == "created_ballot": return ballot else: return None @@ -616,17 +616,15 @@ class BallotDocEvent(DocEvent): active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active")) res = {} - if self.doc.latest_event(BallotDocEvent, type="created_ballot") == self: - - positions = BallotPositionDocEvent.objects.filter(type="changed_ballot_position",ad__in=active_ads, ballot=self).select_related('ad', 'pos').order_by("-time", "-id") - - for pos in positions: - if pos.ad not in res: - res[pos.ad] = pos - - for ad in active_ads: - if ad not in res: - res[ad] = None + positions = BallotPositionDocEvent.objects.filter(type="changed_ballot_position",ad__in=active_ads, ballot=self).select_related('ad', 'pos').order_by("-time", "-id") + + for pos in positions: + if pos.ad not in res: + res[pos.ad] = pos + + for ad in active_ads: + if ad not in res: + res[ad] = None return res def all_positions(self): From b3ed1530158ee5367882ca9228d441d50f2ad21f Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:13:23 +0000 Subject: [PATCH 080/173] Get rid of old in_group template tag, it's been superseded by has_role - Legacy-Id: 6422 --- ietf/doc/templatetags/ietf_filters.py | 8 -------- ietf/templates/base/left_menu.html | 4 ++-- ietf/templates/doc/ballot/approvaltext.html | 2 +- ietf/templates/doc/ballot/lastcalltext.html | 2 +- ietf/templates/doc/draft/edit_info.html | 2 +- ietf/templates/doc/status_change/last_call.html | 2 +- ietf/templates/doc/status_change/status_changes.html | 2 +- 7 files changed, 7 insertions(+), 15 deletions(-) diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 2e7dc8cf6..45b6d0578 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -414,14 +414,6 @@ def equal(x, y): def startswith(x, y): return unicode(x).startswith(y) -# based on http://www.djangosnippets.org/snippets/847/ by 'whiteinge' -@register.filter -def in_group(user, groups): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return has_role(user, groups.replace("Area_Director", "Area Director")) - - return user and user.is_authenticated() and bool(user.groups.filter(name__in=groups.split(',')).values('name')) - @register.filter def has_role(user, role_names): from ietf.ietfauth.decorators import has_role diff --git a/ietf/templates/base/left_menu.html b/ietf/templates/base/left_menu.html index 9b44d11ad..6bc13926b 100644 --- a/ietf/templates/base/left_menu.html +++ b/ietf/templates/base/left_menu.html @@ -89,7 +89,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ISE
            • Submit a draft
            • -{% if user|in_group:"WG Chair" %} +{% if user|has_role:"WG Chair" %}
            • Approve a draft
            • {% endif %} {% get_user_managed_lists user as community_lists %} @@ -101,7 +101,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% else %}
            • Sign in to track drafts
            • {% endif %} -{% if user|in_group:"Area_Director,Secretariat" %} +{% if user|has_role:"Area_Director,Secretariat" %}
            • RFC status changes
            • {% endif %} diff --git a/ietf/templates/doc/ballot/approvaltext.html b/ietf/templates/doc/ballot/approvaltext.html index f58ae42a6..80b8271f7 100644 --- a/ietf/templates/doc/ballot/approvaltext.html +++ b/ietf/templates/doc/ballot/approvaltext.html @@ -26,7 +26,7 @@ form #id_approval_text { {% load ietf_filters %} -{% if user|in_group:"Secretariat" %} +{% if user|has_role:"Secretariat" %}

              {% if can_announce %} Approve ballot diff --git a/ietf/templates/doc/ballot/lastcalltext.html b/ietf/templates/doc/ballot/lastcalltext.html index 135af9b64..574155daf 100644 --- a/ietf/templates/doc/ballot/lastcalltext.html +++ b/ietf/templates/doc/ballot/lastcalltext.html @@ -35,7 +35,7 @@ form #id_last_call_text { {% load ietf_filters %} -{% if user|in_group:"Secretariat" %} +{% if user|has_role:"Secretariat" %}

              {% if can_make_last_call %} Make Last Call diff --git a/ietf/templates/doc/draft/edit_info.html b/ietf/templates/doc/draft/edit_info.html index 6ff175f9b..68851832a 100644 --- a/ietf/templates/doc/draft/edit_info.html +++ b/ietf/templates/doc/draft/edit_info.html @@ -44,7 +44,7 @@ form.edit-info .actions { {{ form.returning_item }} {{ form.returning_item.label_tag }} {{ form.returning_item.errors }} {% endifequal %} {% ifequal field.name "ad" %} - {% if user|in_group:"Area_Director" %} + {% if user|has_role:"Area Director" %} {% endif %} {% endifequal %} diff --git a/ietf/templates/doc/status_change/last_call.html b/ietf/templates/doc/status_change/last_call.html index 6da9ec714..58e156c73 100644 --- a/ietf/templates/doc/status_change/last_call.html +++ b/ietf/templates/doc/status_change/last_call.html @@ -33,7 +33,7 @@ form #id_last_call_text { {% load ietf_filters %} -{% if user|in_group:"Secretariat" %} +{% if user|has_role:"Secretariat" %}

              Make Last Call

              diff --git a/ietf/templates/doc/status_change/status_changes.html b/ietf/templates/doc/status_change/status_changes.html index 5b84dd4ab..02536d1f3 100644 --- a/ietf/templates/doc/status_change/status_changes.html +++ b/ietf/templates/doc/status_change/status_changes.html @@ -4,7 +4,7 @@ {% block content %}

              RFC Status Changes

              -{% if user|in_group:"Area_Director,Secretariat" %} +{% if user|has_role:"Area Director,Secretariat" %}

              Start new RFC status change document

              {% endif %} {% regroup docs by get_state as state_groups %} From 75a2c9ba0d2ce2b9751feb66007edf0be47c090e Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:17:32 +0000 Subject: [PATCH 081/173] Remove idwrapper compatibility code from ballot_icon.py - Legacy-Id: 6423 --- ietf/doc/templatetags/ballot_icon.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/ietf/doc/templatetags/ballot_icon.py b/ietf/doc/templatetags/ballot_icon.py index ddcf01a9f..5325ec1fb 100644 --- a/ietf/doc/templatetags/ballot_icon.py +++ b/ietf/doc/templatetags/ballot_icon.py @@ -67,11 +67,6 @@ def render_ballot_icon(doc, user): if not doc: return "" - # FIXME: temporary backwards-compatibility hack - from ietf.doc.models import Document - if not isinstance(doc, Document): - doc = doc._draft - if not showballoticon(doc): return "" @@ -158,29 +153,7 @@ def ballotposition(doc, user): @register.filter -def my_position(doc, user): - if not has_role(user, "Area Director"): - return None - # FIXME: temporary backwards-compatibility hack - from ietf.doc.models import Document - if not isinstance(doc, Document): - doc = doc._draft - - ballot = doc.active_ballot() - pos = "No Record" - if ballot: - changed_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad__user=user, ballot=ballot) - if changed_pos: - pos = changed_pos.pos.name; - return pos - -@register.filter() def state_age_colored(doc): - # FIXME: temporary backwards-compatibility hack - from ietf.doc.models import Document - if not isinstance(doc, Document): - doc = doc._draft - if doc.type_id == 'draft': if not doc.get_state_slug() in ["active", "rfc"]: # Don't show anything for expired/withdrawn/replaced drafts From 1c8c87f88aa64716660449aebbcc488c5823c88c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 13:19:07 +0000 Subject: [PATCH 082/173] Remove search for "IESG has approved and state has been changed to" from state change doc event searches, these have been split up by a migration and no longer exist - Legacy-Id: 6424 --- ietf/doc/templatetags/ballot_icon.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ietf/doc/templatetags/ballot_icon.py b/ietf/doc/templatetags/ballot_icon.py index 5325ec1fb..18ca432cb 100644 --- a/ietf/doc/templatetags/ballot_icon.py +++ b/ietf/doc/templatetags/ballot_icon.py @@ -173,7 +173,6 @@ def state_age_colored(doc): Q(desc__istartswith="State Changes to ")| Q(desc__istartswith="Sub state has been changed to ")| Q(desc__istartswith="State has been changed to ")| - Q(desc__istartswith="IESG has approved and state has been changed to")| Q(desc__istartswith="IESG process started in state") ).order_by('-time')[0].time.date() except IndexError: From b24a20d328c5f7e2941669190b8e1f91af24066c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 14:31:50 +0000 Subject: [PATCH 083/173] Add test for /iesg/discusses/ - Legacy-Id: 6425 --- ietf/iesg/tests.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 185218070..074c25190 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -8,7 +8,7 @@ from pyquery import PyQuery from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized from ietf.utils.test_data import make_test_data -from ietf.doc.models import Document, DocEvent, TelechatDocEvent, State +from ietf.doc.models import * from ietf.person.models import Person from ietf.group.models import Group from ietf.name.models import StreamName @@ -347,3 +347,23 @@ class DeferUndeferTestCase(django.test.TestCase): def setUp(self): make_test_data() + +class IESGDiscussesTests(django.test.TestCase): + def test_feed(self): + draft = make_test_data() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + + pos = BallotPositionDocEvent() + pos.ballot = draft.latest_event(BallotDocEvent, type="created_ballot") + pos.pos_id = "discuss" + pos.type = "changed_ballot_position" + pos.doc = draft + pos.ad = pos.by = Person.objects.get(user__username="ad") + pos.save() + + r = self.client.get(urlreverse("ietf.iesg.views.discusses")) + self.assertEquals(r.status_code, 200) + + self.assertTrue(draft.name in r.content) + self.assertTrue(pos.ad.plain_name() in r.content) + From 5cf8d5af6a6474c3a23b723c36c4d066410856ef Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 14:33:46 +0000 Subject: [PATCH 084/173] Area_Director -> Area Director in left_menu.html - Legacy-Id: 6426 --- ietf/templates/base/left_menu.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/templates/base/left_menu.html b/ietf/templates/base/left_menu.html index 6bc13926b..b9b2502d5 100644 --- a/ietf/templates/base/left_menu.html +++ b/ietf/templates/base/left_menu.html @@ -101,7 +101,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% else %}
            • Sign in to track drafts
            • {% endif %} -{% if user|has_role:"Area_Director,Secretariat" %} +{% if user|has_role:"Area Director,Secretariat" %}
            • RFC status changes
            • {% endif %} From 5f337e3c70e23848ae5fa28a8be4dffd8af3ed44 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 14:35:30 +0000 Subject: [PATCH 085/173] Remove obsolete testurl.list tests from IESG, the tests in iesg/tests.py together with the test crawler has better coverage - Legacy-Id: 6427 --- ietf/iesg/tests.py | 9 --------- ietf/iesg/testurl.list | 24 ------------------------ 2 files changed, 33 deletions(-) delete mode 100644 ietf/iesg/testurl.list diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 074c25190..69ca388f9 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -242,15 +242,6 @@ class RescheduleOnAgendaTests(django.test.TestCase): self.assertTrue(not draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").returning_item) self.assertEquals(draft.docevent_set.count(), events_before + 1) -class IesgUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) - def doCanonicalize(self, url, content): - if url.startswith("/feed/"): - return canonicalize_feed(content) - else: - return content - class DeferUndeferTestCase(django.test.TestCase): def helper_test_defer(self,name): diff --git a/ietf/iesg/testurl.list b/ietf/iesg/testurl.list deleted file mode 100644 index 1f7734032..000000000 --- a/ietf/iesg/testurl.list +++ /dev/null @@ -1,24 +0,0 @@ - -301 /iesg/telechat/ -301 /iesg/telechat/y/2007/ -301 /iesg/telechat/y/2007/apr/ -301 /iesg/telechat/354/ - -200 /iesg/agenda/ -200 /iesg/agenda/agenda.txt -200 /iesg/agenda/scribe_template.html -200 /iesg/agenda/documents.txt -200 /iesg/agenda/documents/ -200 /iesg/discusses/ - -302 /iesg/agenda/moderator_package.html -302 /iesg/agenda/agenda_package.txt -200 /iesg/_test/moderator_package.html -200 /iesg/_test/agenda_package.txt - -200 /iesg/ann/ind/ -200 /iesg/ann/new/ -# This takes ~ 300s: -#200 /iesg/ann/prev/ - -200 /feed/iesg-agenda/ From a17c07f28254222a6b641bd25e2be0839e95dff3 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 14:37:11 +0000 Subject: [PATCH 086/173] Remove unused fixtures/sieve-charter.txt - Legacy-Id: 6428 --- ietf/iesg/fixtures/sieve-charter.txt | 143 --------------------------- 1 file changed, 143 deletions(-) delete mode 100644 ietf/iesg/fixtures/sieve-charter.txt diff --git a/ietf/iesg/fixtures/sieve-charter.txt b/ietf/iesg/fixtures/sieve-charter.txt deleted file mode 100644 index 389aef8d0..000000000 --- a/ietf/iesg/fixtures/sieve-charter.txt +++ /dev/null @@ -1,143 +0,0 @@ -Sieve Mail Filtering Language (sieve) -------------------------------------- -Current Status: Active -Last updated: 2010-05-07 - -Chairs: -Cyrus Daboo -Aaron Stone - -Applications Area Directors: -Alexey Melnikov -Peter Saint-Andre - -Applications Area Advisor: -Alexey Melnikov - -Mailing Lists: -General Discussion: sieve@ietf.org -To Subscribe: sieve-request@ietf.org -Archive: -http://www.ietf.org/mail-archive/web/sieve/current/maillist.html - -Description of Working Group: - -The SIEVE email filtering language is specified in RFC 5228, together -with a number of extensions. - -The SIEVE working group is being re-chartered to: - -(1) Finish work on existing in-progress Working Group documents: -(a) External lists (draft-ietf-sieve-external-lists) -(b) Notify SIP (draft-ietf-sieve-notify-sip-message) -(c) RegEx (draft-ietf-sieve-regex) -(d) Include/multi-script (draft-ietf-sieve-include) -(e) Sieve in IMAP (draft-ietf-sieve-imap-sieve) - -(2) Finalize and publish the following SIEVE extensions as proposed -standards: -(a) General Auto-reply (draft-george-sieve-autoreply) -(b) Notify presence (draft-george-sieve-notify-presence) -(c) Vacation time (draft-george-sieve-vacation-time) -(d) Convert messages (draft-melnikov-sieve-convert) - -Additional drafts may be added to this list, but only via a charter -revision. There must also be demonstrable willingness in the SIEVE -development community to actually implement a given extension before it -can be added to this charter. - -(3) Work on a specification for iCalendar and vCard extraction, and -cooperate with the VCARDDAV WG for address book tests in Sieve. - -(4) Work on a specification to describe how EAI/IDN issues should be -handled in SIEVE. - -(5) Work on a "Benefits of SIEVE" guide for client and server vendors -that: -(a) Describes the SIEVE protocol and its suite of extensions. -(b) Explains the benefits of server-side filtering in practical terms. -(c) Shows how client-side filtering can be migrated to SIEVE. - -(6) Produce one or more informational RFCs containing a set of test -scripts and test email messages that are to be filtered by the scripts, -and the expected results of that filtering. This will serve as the basis -of a interoperability test suite to help determine the suitability of -moving the base specification and selected extensions to Draft status. - - -Goals and Milestones: -Done - Submit revised variables draft. -Done - Submit revised vacation draft. -Done - WG last call for variables draft. -Done - Initial submission of RFC 3028bis. -Done - WG last call for RFC 3028bis. -Done - Initial submission of revised relational draft. -Done - Initial submission of revised subaddress draft. -Done - Initial submission of revised spamtest/virustest draft. -Done - Submit revised editheader draft. -Done - Submit revised imapflags draft. -Done - WG last call of revised subaddress draft. -Done - Submit revised body test draft. -Done - Submit revised reject before delivery draft. -Done - WG last call for editheader draft. -Done - WG last call for body test draft. -Done - WG last call for refuse draft -Done - WG last call of revised spamtest draft -Done - Submit variables draft to IESG -Done - Submit revised loop draft -Done - Submit revised notification action draft -Done - WG last call of revised relational draft -Done - WG last call for imap-flags draft -Done - WG last call for vacation draft -Done - WG last call of revised subaddress draft -Done - Submit revised relational draft to IESG -Done - Submit vacation draft to IESG -Done - Submit revised subaddress draft to IESG -Done - Submit imapflags draft to IESG -Done - Submit revised spamtest draft to IESG -Done - Submit 3028bis to IESG -Done - Submit editheader draft to IESG -Done - Submit body test draft to IESG -Done - WG last call for notification action draft -Done - Submit notification action draft to IESG -Done - Submit refuse-reject to IESG -Done - Submit notify-mailto to IESG -Done - Submit mime-loops to IESG -Done - WGLC iHave -Done - WGLC Notary -Done - Submit iHave to IESG -Done - Submit Notary to IESG -Done - WGLC sieve-in-xml -Done - Submit sieve-in-xml to IESG -Done - WGLC ManageSIEVE -Done - Submit ManageSIEVE to IESG -Done - WGLC Notify-sip -Done - WGLC Metadata -Done - Submit Metadata to IESG -Done - Publish refuse/reject - RFC 5429 -Done - Publish notify base spec - RFC 5435 -Done - Publish notify mailto extension - RFC 5436 -Done - Publish notify xmpp extension - RFC 5437 -Done - Publish ihave - RFC 5463 -Done - Publish meta-data - RFC 5490 -Done - Publish mime loops - RFC 5703 -Done - Publish Sieve in XML - RFC 5784 -Done - Revised RegEx draft -Apr 2010 - Revised Include/multi-script draft -Apr 2010 - WGLC external-lists -May 2010 - WGLC Include/multi-script -May 2010 - Submit external-lists to IESG -Jun 2010 - Submit Include/multi-script to IESG -Jun 2010 - WGLC Notify-SIP -Jul 2010 - Initial eai-issues draft -Jul 2010 - Submit Notify-SIP to IESG -Aug 2010 - WGLC RegEx -Aug 2010 - Initial test-scripts draft -Aug 2010 - Initial benefits draft -Sep 2010 - Submit RegEx to IESG -Oct 2010 - WGLC eai-issues -Nov 2010 - Submit eai-issues to IESG -Nov 2010 - WGLC benefits -Jan 2011 - Submit benefits to IESG -Mar 2011 - WGLC test-scripts -Apr 2011 - Submit test-scripts to IESG \ No newline at end of file From 87aaf4a47e86ca03fbb1f43adc72ba3381d6b156 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 14:38:50 +0000 Subject: [PATCH 087/173] Remove unused idtracker import from iesg/models.py - Legacy-Id: 6429 --- ietf/iesg/models.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ietf/iesg/models.py b/ietf/iesg/models.py index afed15b62..a797f9ab6 100644 --- a/ietf/iesg/models.py +++ b/ietf/iesg/models.py @@ -32,10 +32,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import datetime + from django.db import models from django.conf import settings -from ietf.idtracker.models import Acronym -import datetime class TelechatAgendaItem(models.Model): TYPE_CHOICES = ( @@ -48,10 +48,7 @@ class TelechatAgendaItem(models.Model): text = models.TextField(blank=True, db_column='template_text') type = models.IntegerField(db_column='template_type', choices=TYPE_CHOICES, default=3) title = models.CharField(max_length=255, db_column='template_title') - #The following fields are apparently not used - #note = models.TextField(null=True,blank=True) - #discussed_status_id = models.IntegerField(null=True, blank=True) - #decision = models.TextField(null=True,blank=True) + def __unicode__(self): type_name = self.TYPE_CHOICES_DICT.get(self.type, str(self.type)) return u'%s: %s' % (type_name, self.title or "") @@ -65,6 +62,7 @@ class Telechat(models.Model): management_issue = models.TextField(blank=True) frozen = models.IntegerField(null=True, blank=True) mi_frozen = models.IntegerField(null=True, blank=True) + class Meta: db_table = u'telechat' From ec7e01481dc11cb11faf0967477947843fbb329d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 11 Oct 2013 14:50:30 +0000 Subject: [PATCH 088/173] Fix a potential bug with milestones_needing_review and ad-less groups - Legacy-Id: 6430 --- ietf/iesg/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 893371abb..095197b25 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -423,7 +423,7 @@ def discusses(request): def milestones_needing_review(request): # collect milestones, grouped on AD and group ads = {} - for m in GroupMilestone.objects.filter(state="review").exclude(group__state="concluded", group__ad=None).distinct().select_related("group", "group__ad"): + for m in GroupMilestone.objects.filter(state="review").exclude(group__state="concluded").exclude(group__ad=None).distinct().select_related("group", "group__ad"): groups = ads.setdefault(m.group.ad, {}) milestones = groups.setdefault(m.group, []) milestones.append(m) From de2ed527b4f619671827b0c346fab4e325d03674 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 16 Oct 2013 12:04:27 +0000 Subject: [PATCH 089/173] Fix indentation problem with ballot writeup in Secretariat telechat tool and fix problem with unescaped << and >> - Legacy-Id: 6453 --- ietf/secr/templates/telechat/doc.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ietf/secr/templates/telechat/doc.html b/ietf/secr/templates/telechat/doc.html index a26d39fd7..793bf308f 100644 --- a/ietf/secr/templates/telechat/doc.html +++ b/ietf/secr/templates/telechat/doc.html @@ -7,8 +7,8 @@ {% block subsection %}
              {% if document %} - {% if not nav_start %}<< Previous{% endif %} - {% if not nav_end %}Next >>{% endif %} + {% if not nav_start %}« Previous{% endif %} + {% if not nav_end %}Next »{% endif %}

              {% for line in header %} @@ -64,7 +64,7 @@

              Ballot Writeup

              -    {{ writeup }}
              +{{ writeup }}
                   
              From 10dceef26d98cbaa2466e092ed15452d5066b560 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 16 Oct 2013 13:07:09 +0000 Subject: [PATCH 090/173] Refactor IESG agenda pages, put the agenda structure in a helper function in agenda.py, build the agenda up around this structure, and reuse this everywhere instead of duplicating the structure in various templates and the JSON view - this also fixes a couple of minor bugs where the copy-pasted copies were not entirely in sync - Legacy-Id: 6454 --- ietf/doc/templatetags/ietf_filters.py | 4 + ietf/iesg/agenda.py | 212 ++++++----- ietf/iesg/tests.py | 4 +- ietf/iesg/views.py | 346 ++++++++++-------- ietf/secr/telechat/views.py | 70 ++-- .../templates/telechat/base_telechat.html | 87 +---- ietf/templates/iesg/agenda.html | 175 ++++----- ietf/templates/iesg/agenda.txt | 72 +--- ietf/templates/iesg/agenda_charter.html | 17 + ietf/templates/iesg/agenda_charter.txt | 2 + ietf/templates/iesg/agenda_conflict_doc.html | 138 ++----- ietf/templates/iesg/agenda_conflict_doc.txt | 55 +-- ietf/templates/iesg/agenda_doc.html | 160 +++----- ietf/templates/iesg/agenda_doc.txt | 45 +-- ietf/templates/iesg/agenda_documents.html | 100 +---- ietf/templates/iesg/agenda_outline_23.html | 183 --------- ietf/templates/iesg/agenda_outline_4.html | 63 ---- ietf/templates/iesg/agenda_package.txt | 6 +- ietf/templates/iesg/agenda_wg.html | 55 --- ietf/templates/iesg/agenda_wg.txt | 42 --- ...derator_wg.html => moderator_charter.html} | 23 +- .../iesg/moderator_conflict_doc.html | 55 ++- ietf/templates/iesg/moderator_doc.html | 63 ++-- ietf/templates/iesg/moderator_package.html | 147 ++++---- ietf/templates/iesg/scribe_conflict_doc.html | 113 +----- ietf/templates/iesg/scribe_doc.html | 114 +----- ietf/templates/iesg/scribe_doc2.html | 57 --- ietf/templates/iesg/scribe_doc_ballot.html | 33 ++ ietf/templates/iesg/scribe_template.html | 64 ++-- 29 files changed, 836 insertions(+), 1669 deletions(-) create mode 100644 ietf/templates/iesg/agenda_charter.html create mode 100644 ietf/templates/iesg/agenda_charter.txt delete mode 100644 ietf/templates/iesg/agenda_outline_23.html delete mode 100644 ietf/templates/iesg/agenda_outline_4.html delete mode 100644 ietf/templates/iesg/agenda_wg.html delete mode 100644 ietf/templates/iesg/agenda_wg.txt rename ietf/templates/iesg/{moderator_wg.html => moderator_charter.html} (89%) delete mode 100644 ietf/templates/iesg/scribe_doc2.html create mode 100644 ietf/templates/iesg/scribe_doc_ballot.html diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 45b6d0578..5c19edb59 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -476,6 +476,10 @@ def statehelp(state): url = urlreverse("state_help", kwargs=dict(type=state.type_id)) + "#" + state.slug return mark_safe('?' % (url, tooltip)) +@register.filter +def sectionlevel(section_number): + return section_number.count(".") + 1 + def _test(): import doctest doctest.testmod() diff --git a/ietf/iesg/agenda.py b/ietf/iesg/agenda.py index 3d6d8dea6..48a57483a 100644 --- a/ietf/iesg/agenda.py +++ b/ietf/iesg/agenda.py @@ -2,6 +2,9 @@ import codecs, re, os, datetime +# FIXME: once we're on Python 2.7, replace with: from collections import OrderedDict +from django.utils.datastructures import SortedDict as OrderedDict + from django.http import Http404 from django.conf import settings @@ -17,7 +20,8 @@ def get_agenda_date(date=None): return datetime.date.today() else: try: - return TelechatDate.objects.active().get(date=datetime.datetime.strptime(date, "%Y-%m-%d").date()).date + # FIXME: .active() + return TelechatDate.objects.all().get(date=datetime.datetime.strptime(date, "%Y-%m-%d").date()).date except (ValueError, TelechatDate.DoesNotExist): raise Http404 @@ -30,66 +34,125 @@ def get_doc_section(doc): g = doc.group_acronym() if g and str(g) != 'none': - s = s + "1" - elif (s == "3") and doc.stream_id in ("ise","irtf"): - s = s + "3" + s += ".1" + elif s == "3" and doc.stream_id in ("ise","irtf"): + s += ".3" else: - s = s + "2" + s += ".2" if doc.get_state_slug() != "rfc" and doc.get_state_slug('draft-iesg') not in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"): - s = s + "3" + s += ".3" elif doc.returning_item(): - s = s + "2" + s += ".2" else: - s = s + "1" + s += ".1" + elif doc.type_id == 'charter': - s = get_wg_section(doc.group) + s = "4" + if doc.group.state_id in ('active', 'dormant'): + s += ".2" + else: + s += ".1" + if doc.get_state_slug() in ('extrev', 'iesgrev'): + s += '.2' + else: + s += '.1' + elif doc.type_id == 'statchg': protocol_action = False for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')): if relation.relationship.slug in ('tops','tois') or relation.target.document.std_level.slug in ('std','ds','ps'): protocol_action = True if protocol_action: - s="23" + s = "2.3" else: - s="33" + s = "3.3" if doc.get_state_slug() not in ("iesgeval", "defer", "appr-pr", "appr-pend", "appr-sent"): - s = s + "3" + s += ".3" elif doc.returning_item(): - s = s + "2" + s += ".2" else: - s = s + "1" + s += ".1" elif doc.type_id == 'conflrev': if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'): - s = "343" + s = "3.4.3" elif doc.returning_item(): - s = "342" + s = "3.4.2" else: - s = "341" + s = "3.4.1" return s -def get_wg_section(wg): - s = "" - charter_slug = None - if wg.charter: - charter_slug = wg.charter.get_state_slug() - if wg.state_id in ['active','dormant']: - if charter_slug in ['extrev','iesgrev']: - s = '422' - else: - s = '421' - else: - if charter_slug in ['extrev','iesgrev']: - s = '412' - else: - s = '411' - return s +def agenda_sections(): + return OrderedDict([ + ('1', {'title':"Administrivia"}), + ('1.1', {'title':"Roll Call"}), + ('1.2', {'title':"Bash the Agenda"}), + ('1.3', {'title':"Approval of the Minutes of Past Telechats"}), + ('1.4', {'title':"List of Remaining Action Items from Last Telechat"}), + ('2', {'title':"Protocol Actions"}), + ('2.1', {'title':"WG Submissions"}), + ('2.1.1', {'title':"New Items", 'docs': []}), + ('2.1.2', {'title':"Returning Items", 'docs':[]}), + ('2.1.3', {'title':"For Action", 'docs':[]}), + ('2.2', {'title':"Individual Submissions"}), + ('2.2.1', {'title':"New Items", 'docs':[]}), + ('2.2.2', {'title':"Returning Items", 'docs':[]}), + ('2.2.3', {'title':"For Action", 'docs':[]}), + ('2.3', {'title':"Status Changes"}), + ('2.3.1', {'title':"New Items", 'docs':[]}), + ('2.3.2', {'title':"Returning Items", 'docs':[]}), + ('2.3.3', {'title':"For Action", 'docs':[]}), + ('3', {'title':"Document Actions"}), + ('3.1', {'title':"WG Submissions"}), + ('3.1.1', {'title':"New Items", 'docs':[]}), + ('3.1.2', {'title':"Returning Items", 'docs':[]}), + ('3.1.3', {'title':"For Action", 'docs':[]}), + ('3.2', {'title':"Individual Submissions Via AD"}), + ('3.2.1', {'title':"New Items", 'docs':[]}), + ('3.2.2', {'title':"Returning Items", 'docs':[]}), + ('3.2.3', {'title':"For Action", 'docs':[]}), + ('3.3', {'title':"Status Changes"}), + ('3.3.1', {'title':"New Items", 'docs':[]}), + ('3.3.2', {'title':"Returning Items", 'docs':[]}), + ('3.3.3', {'title':"For Action", 'docs':[]}), + ('3.4', {'title':"IRTF and Independent Submission Stream Documents"}), + ('3.4.1', {'title':"New Items", 'docs':[]}), + ('3.4.2', {'title':"Returning Items", 'docs':[]}), + ('3.4.3', {'title':"For Action", 'docs':[]}), + ('4', {'title':"Working Group Actions"}), + ('4.1', {'title':"WG Creation"}), + ('4.1.1', {'title':"Proposed for IETF Review", 'docs':[]}), + ('4.1.2', {'title':"Proposed for Approval", 'docs':[]}), + ('4.2', {'title':"WG Rechartering"}), + ('4.2.1', {'title':"Under Evaluation for IETF Review", 'docs':[]}), + ('4.2.2', {'title':"Proposed for Approval", 'docs':[]}), + ('5', {'title':"IAB News We Can Use"}), + ('6', {'title':"Management Issues"}), + ('7', {'title':"Working Group News"}), + ]) -def agenda_docs(date): - matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).select_related("stream").distinct() +def fill_in_agenda_administrivia(date, sections): + extra_info_files = ( + ("1.1", "roll_call", settings.IESG_ROLL_CALL_FILE), + ("1.3", "minutes", settings.IESG_MINUTES_FILE), + ("1.4", "action_items", settings.IESG_TASK_FILE), + ) - docmatches = [] - + for s, key, filename in extra_info_files: + try: + with codecs.open(filename, 'r', 'utf-8', 'replace') as f: + t = f.read().strip() + except IOError: + t = u"(Error reading %s)" % filename + + sections[s]["text"] = t + +def fill_in_agenda_docs(date, sections, matches=None): + if not matches: + matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date) + matches = matches.select_related("stream", "group").distinct() + + docs = [] for doc in matches: if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: continue @@ -112,59 +175,42 @@ def agenda_docs(date): e = doc.latest_event(ConsensusDocEvent, type="changed_consensus") if e: doc.consensus = "Yes" if e.consensus else "No" - elif doc.type_id=='conflrev': + elif doc.type_id == "conflrev": doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document + elif doc.type_id == "charter": + #if doc.group.state_id not in ("proposed", "active"): + # continue - docmatches.append(doc) + doc.group.txt_link = settings.CHARTER_TXT_URL + "%s-%s.txt" % (doc.canonical_name(), doc.rev) + + num = get_doc_section(doc) + if num: # and num in sections + sections[num]["docs"].append(doc) + + # prune empty "For action" sections + empty_for_action = [num for num, section in sections.iteritems() + if section["title"] == "For Action" and not section["docs"]] + for num in empty_for_action: + del sections[num] # Be careful to keep this the same as what's used in agenda_documents - docmatches.sort(key=lambda d: d.balloting_started) - - res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4)) - for k in range(1,4): - res['s34%d'%k]=[] - for doc in docmatches: - section_key = "s" + get_doc_section(doc) - if section_key not in res: - res[section_key] = [] - res[section_key].append(doc) - return res + for s in sections.itervalues(): + if "docs" in s: + s["docs"].sort(key=lambda d: d.balloting_started) -def agenda_wg_actions(date): - res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4)) - charters = Document.objects.filter(type="charter", docevent__telechatdocevent__telechat_date=date).select_related("group").distinct() - charters = charters.filter(group__state__slug__in=["proposed","active"]) - for c in charters: - if c.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: - continue - - c.group.txt_link = settings.CHARTER_TXT_URL + "%s-%s.txt" % (c.canonical_name(), c.rev) - - section_key = "s" + get_wg_section(c.group) - if section_key not in res: - res[section_key] = [] - res[section_key].append(c) - return res - -def agenda_management_issues(date): - return TelechatAgendaItem.objects.filter(type=3).order_by('id') +def fill_in_agenda_management_issues(date, sections): + s = "6.%s" + for i, item in enumerate(TelechatAgendaItem.objects.filter(type=3).order_by('id'), start=1): + sections[s % i] = { "title": item.title, "text": item.text } def agenda_data(request, date=None): """Return a dict with the different IESG telechat agenda components.""" date = get_agenda_date(date) - docs = agenda_docs(date) - mgmt = agenda_management_issues(date) - wgs = agenda_wg_actions(date) - data = {'date':str(date), 'docs':docs,'mgmt':mgmt,'wgs':wgs} - for key, filename in {'action_items':settings.IESG_TASK_FILE, - 'roll_call':settings.IESG_ROLL_CALL_FILE, - 'minutes':settings.IESG_MINUTES_FILE}.items(): - try: - f = codecs.open(filename, 'r', 'utf-8', 'replace') - text = f.read().strip() - f.close() - data[key] = text - except IOError: - data[key] = "(Error reading "+key+")" - return data + sections = agenda_sections() + + fill_in_agenda_administrivia(date, sections) + fill_in_agenda_docs(date, sections) + fill_in_agenda_management_issues(date, sections) + + return { 'date': date.isoformat(), 'sections': sections } diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 69ca388f9..3cce3b115 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -229,10 +229,12 @@ class RescheduleOnAgendaTests(django.test.TestCase): r = self.client.post(url, { '%s-telechat_date' % form_id: d.isoformat(), '%s-clear_returning_item' % form_id: "1" }) - self.assertEquals(r.status_code, 200) + self.assertEquals(r.status_code, 302) # check that it moved below the right header in the DOM on the # agenda docs page + r = self.client.get(url) + self.assertEquals(r.status_code, 200) d_header_pos = r.content.find("IESG telechat %s" % d.isoformat()) draft_pos = r.content.find(draft.name) self.assertTrue(d_header_pos < draft_pos) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 095197b25..9669b70f6 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -32,15 +32,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import codecs, re, os, glob -import datetime +import codecs, re, os, glob, datetime import tarfile, StringIO, time +import itertools from django.views.generic.simple import direct_to_template from django.core.urlresolvers import reverse as urlreverse from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseRedirect from django.template import RequestContext, Context, loader -from django.shortcuts import render_to_response, get_object_or_404 +from django.shortcuts import render_to_response, get_object_or_404, redirect from django.conf import settings from django.utils import simplejson as json from django.db import models @@ -84,166 +84,185 @@ def review_decisions(request, year=None): context_instance=RequestContext(request)) def agenda_json(request, date=None): - date = get_agenda_date(date) + data = agenda_data(request, date) - data = {'telechat-date':str(date), - 'as-of':str(datetime.datetime.utcnow()), - 'sections':{}} - data['sections']['1'] = {'title':"Administrivia"} - data['sections']['1.1'] = {'title':"Roll Call"} - data['sections']['1.2'] = {'title':"Bash the Agenda"} - data['sections']['1.3'] = {'title':"Approval of the Minutes of Past Telechats"} - data['sections']['1.4'] = {'title':"List of Remaining Action Items from Last Telechat"} - data['sections']['2'] = {'title':"Protocol Actions"} - data['sections']['2.1'] = {'title':"WG Submissions"} - data['sections']['2.1.1'] = {'title':"New Items", 'docs':[]} - data['sections']['2.1.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['2.2'] = {'title':"Individual Submissions"} - data['sections']['2.2.1'] = {'title':"New Items", 'docs':[]} - data['sections']['2.2.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['2.3'] = {'title':"Individual Submissions"} - data['sections']['2.3.1'] = {'title':"New Items", 'docs':[]} - data['sections']['2.3.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3'] = {'title':"Document Actions"} - data['sections']['3.1'] = {'title':"WG Submissions"} - data['sections']['3.1.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.1.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3.2'] = {'title':"Individual Submissions Via AD"} - data['sections']['3.2.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.2.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3.3'] = {'title':"Status Changes"} - data['sections']['3.3.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.3.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3.4'] = {'title':"IRTF and Independent Submission Stream Documents"} - data['sections']['3.4.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.4.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['4'] = {'title':"Working Group Actions"} - data['sections']['4.1'] = {'title':"WG Creation"} - data['sections']['4.1.1'] = {'title':"Proposed for IETF Review", 'wgs':[]} - data['sections']['4.1.2'] = {'title':"Proposed for Approval", 'wgs':[]} - data['sections']['4.2'] = {'title':"WG Rechartering"} - data['sections']['4.2.1'] = {'title':"Under Evaluation for IETF Review", 'wgs':[]} - data['sections']['4.2.2'] = {'title':"Proposed for Approval", 'wgs':[]} - data['sections']['5'] = {'title':"IAB News We Can Use"} - data['sections']['6'] = {'title':"Management Issues"} - data['sections']['7'] = {'title':"Working Group News"} + res = { + "telechat-date": str(data["date"]), + "as-of": str(datetime.datetime.utcnow()), + "sections": {}, + } - docs = agenda_docs(date) - for section in docs.keys(): - # in case the document is in a state that does not have an agenda section - if section == 's': + for num, section in data["sections"].iteritems(): + s = res["sections"][num] = { + "title": section["title"], + } + + if "docs" not in section: continue - s = str(".".join(list(section)[1:])) - if s[0:1] == '4': - # ignore these; not sure why they are included by agenda_docs - continue + docs = section["docs"] - if not docs[section]: - continue + if "4" <= num < "5": + # charters + s["wgs"] = [] - # If needed, add a "For Action" section to agenda - if s[4:5] == '3': - data['sections'][s] = {'title':"For Action", 'docs':[]} + for doc in docs: + wginfo = { + 'docname': doc.canonical_name(), + 'rev': doc.rev, + 'wgname': doc.group.name, + 'acronym': doc.group.acronym, + 'ad': doc.group.ad.name if doc.group.ad else None, + } - for d in docs[section]: - docinfo = {'docname':d.canonical_name(), - 'title':d.title, - 'ad':d.ad.name if d.ad else None } - if d.note: - docinfo['note'] = d.note - defer = d.active_defer_event() - if defer: - docinfo['defer-by'] = defer.by.name - docinfo['defer-at'] = str(defer.time) - if d.type_id == "draft": - docinfo['rev'] = d.rev - docinfo['intended-std-level'] = str(d.intended_std_level) - if d.rfc_number(): - docinfo['rfc-number'] = d.rfc_number() + # consider moving the charters to "docs" like the other documents + s['wgs'].append(wginfo) + else: + # other documents + s["docs"] = [] - iana_state = d.get_state("draft-iana-review") - if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"): - docinfo['iana-review-state'] = str(iana_state) + for doc in docs: + docinfo = { + 'docname':doc.canonical_name(), + 'title':doc.title, + 'ad':doc.ad.name if doc.ad else None, + } - if d.get_state_slug("draft-iesg") == "lc": - e = d.latest_event(LastCallDocEvent, type="sent_last_call") + if doc.note: + docinfo['note'] = doc.note + defer = doc.active_defer_event() + if defer: + docinfo['defer-by'] = defer.by.name + docinfo['defer-at'] = str(defer.time) + if doc.type_id == "draft": + docinfo['rev'] = doc.rev + docinfo['intended-std-level'] = str(doc.intended_std_level) + if doc.rfc_number(): + docinfo['rfc-number'] = doc.rfc_number() + + iana_state = doc.get_state("draft-iana-review") + if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"): + docinfo['iana-review-state'] = str(iana_state) + + if doc.get_state_slug("draft-iesg") == "lc": + e = doc.latest_event(LastCallDocEvent, type="sent_last_call") + if e: + docinfo['lastcall-expires'] = e.expires.strftime("%Y-%m-%d") + + docinfo['consensus'] = None + e = doc.latest_event(ConsensusDocEvent, type="changed_consensus") if e: - docinfo['lastcall-expires'] = e.expires.strftime("%Y-%m-%d") + docinfo['consensus'] = e.consensus + elif doc.type_id == 'conflrev': + docinfo['rev'] = doc.rev + td = doc.relateddocument_set.get(relationship__slug='conflrev').target.document + docinfo['target-docname'] = td.canonical_name() + docinfo['target-title'] = td.title + docinfo['target-rev'] = td.rev + docinfo['intended-std-level'] = str(td.intended_std_level) + docinfo['stream'] = str(td.stream) + else: + # XXX check this -- is there nothing to set for + # all other documents here? + pass - docinfo['consensus'] = None - e = d.latest_event(ConsensusDocEvent, type="changed_consensus") - if e: - docinfo['consensus'] = e.consensus - elif d.type_id == 'conflrev': - docinfo['rev'] = d.rev - td = d.relateddocument_set.get(relationship__slug='conflrev').target.document - docinfo['target-docname'] = td.canonical_name() - docinfo['target-title'] = td.title - docinfo['target-rev'] = td.rev - docinfo['intended-std-level'] = str(td.intended_std_level) - docinfo['stream'] = str(td.stream) - else: - # XXX check this -- is there nothing to set for - # all other documents here? - pass - data['sections'][s]['docs'] += [docinfo, ] + s["docs"].append(docinfo) - wgs = agenda_wg_actions(date) - for section in wgs.keys(): - # in case the charter is in a state that does not have an agenda section - if section == 's': - continue - - s = str(".".join(list(section)[1:])) - if s[0:1] != '4': - # ignore these; not sure why they are included by agenda_wg_actions - continue - - if not wgs[section]: - continue - - for doc in wgs[section]: - wginfo = {'docname': doc.canonical_name(), - 'rev': doc.rev, - 'wgname': doc.group.name, - 'acronym': doc.group.acronym, - 'ad': doc.group.ad.name if doc.group.ad else None} - data['sections'][s]['wgs'] += [wginfo, ] - - mgmt = agenda_management_issues(date) - num = 0 - for m in mgmt: - num += 1 - data['sections']["6.%d" % num] = {'title':m.title} - - return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain') + return HttpResponse(json.dumps(res, indent=2), mimetype='text/plain') def agenda(request, date=None): data = agenda_data(request, date) - data['settings'] = settings - return render_to_response("iesg/agenda.html", data, context_instance=RequestContext(request)) + + if has_role(request.user, ["Area Director", "IAB Chair", "Secretariat"]): + data["sections"]["1.1"]["title"] = data["sections"]["1.1"]["title"].replace("Roll Call", 'Roll Call') + data["sections"]["1.3"]["title"] = data["sections"]["1.3"]["title"].replace("Minutes", 'Minutes') + + return render_to_response("iesg/agenda.html", { + "date": data["date"], + "sections": sorted(data["sections"].iteritems()), + "settings": settings, + }, context_instance=RequestContext(request)) def agenda_txt(request, date=None): data = agenda_data(request, date) - return render_to_response("iesg/agenda.txt", data, context_instance=RequestContext(request), mimetype="text/plain") + return render_to_response("iesg/agenda.txt", { + "date": data["date"], + "sections": sorted(data["sections"].iteritems()), + }, context_instance=RequestContext(request), mimetype="text/plain") def agenda_scribe_template(request, date=None): - date = get_agenda_date(date) - docs = agenda_docs(date) - return render_to_response('iesg/scribe_template.html', { 'date':str(date), 'docs':docs }, context_instance=RequestContext(request) ) + data = agenda_data(request, date) + sections = sorted((num, section) for num, section in data["sections"].iteritems() if "2" <= num < "4") + appendix_docs = [] + for num, section in sections: + if "docs" in section: + # why are we here including documents that have no discuss/comment? + appendix_docs.extend(section["docs"]) + return render_to_response("iesg/scribe_template.html", { + "date": data["date"], + "sections": sections, + "appendix_docs": appendix_docs, + }, context_instance=RequestContext(request) ) @role_required('Area Director', 'Secretariat') def agenda_moderator_package(request, date=None): + """Output telechat agenda with one page per section, with each + document in its own section.""" data = agenda_data(request, date) - data['ads'] = sorted(Person.objects.filter(role__name="ad", role__group__state="active"), - key=lambda p: p.name_parts()[3]) - return render_to_response("iesg/moderator_package.html", data, context_instance=RequestContext(request)) + + def leaf_section(num, section): + return not (num == "1" + or "2" <= num < "5" and "docs" not in section + or (num == "6" and "6.1" not in data["sections"])) + + # sort and prune non-leaf headlines + sections = sorted((num, section) for num, section in data["sections"].iteritems() + if leaf_section(num, section)) + + # add parents field to each section + for num, s in sections: + s["parents"] = [] + split = num.split(".") + + for i in xrange(num.count(".")): + parent_num = ".".join(split[:i + 1]) + parent = data["sections"].get(parent_num) + if parent: + s["parents"].append((parent_num, parent)) + + + # put each document in its own section + flattened_sections = [] + for num, s in sections: + if "2" <= num < "5" and "docs" in s and s["docs"]: + for i, d in enumerate(s["docs"], start=1): + flattened_sections.append((num, { + "title": s["title"] + " (%s of %s)" % (i, len(s["docs"])), + "doc": d, + "parents": s["parents"], + })) + else: + flattened_sections.append((num, s)) + + # add ads + data["sections"]["7"]["ads"] = sorted(Person.objects.filter(role__name="ad", role__group__state="active"), + key=lambda p: p.name_parts()[3]) + + return render_to_response("iesg/moderator_package.html", { + "date": data["date"], + "sections": flattened_sections, + }, context_instance=RequestContext(request)) @role_required('Area Director', 'Secretariat') def agenda_package(request, date=None): - data = agenda_data(request) - return render_to_response("iesg/agenda_package.txt", data, context_instance=RequestContext(request), mimetype='text/plain') + data = agenda_data(request, date) + return render_to_response("iesg/agenda_package.txt", { + "date": data["date"], + "sections": sorted(data["sections"].iteritems()), + "roll_call": data["sections"]["1.1"]["text"], + "minutes": data["sections"]["1.3"]["text"], + "management_items": [(num, section) for num, section in data["sections"].iteritems() if "6" < num < "7"], + }, context_instance=RequestContext(request), mimetype='text/plain') def agenda_documents_txt(request): @@ -293,7 +312,7 @@ class RescheduleForm(forms.Form): self.fields['telechat_date'].choices = choices -def handle_reschedule_form(request, doc, dates): +def handle_reschedule_form(request, doc, dates, status): initial = dict(telechat_date=doc.telechat_date()) formargs = dict(telechat_dates=dates, @@ -307,6 +326,8 @@ def handle_reschedule_form(request, doc, dates): False if form.cleaned_data['clear_returning_item'] else None) doc.time = datetime.datetime.now() doc.save() + + status["changed"] = True else: form = RescheduleForm(**formargs) @@ -315,37 +336,40 @@ def handle_reschedule_form(request, doc, dates): def agenda_documents(request): dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4]) - docs = [] - for d in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).select_related().distinct(): - if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date in dates: - docs.append(d) - e = d.latest_event(type="started_iesg_process") - d.balloting_started = e.time if e else datetime.datetime.min - docs.sort(key=lambda d: d.balloting_started) + docs_by_date = dict((d, []) for d in dates) + for doc in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).select_related("stream", "group").distinct(): + d = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date + if d in docs_by_date: + docs_by_date[d].append(doc) - for i in docs: - i.reschedule_form = handle_reschedule_form(request, i, dates) + reschedule_status = { "changed": False } - # some may have been taken off the schedule by the reschedule form - docs = [d for d in docs if d.telechat_date() in dates] + for i in itertools.chain(*docs_by_date.values()): + i.reschedule_form = handle_reschedule_form(request, i, dates, reschedule_status) + + if reschedule_status["changed"]: + # if any were changed, redirect so the browser history is preserved + return redirect("ietf.iesg.views.agenda_documents") telechats = [] for date in dates: - matches = filter(lambda x: x.telechat_date() == date, docs) - res = {} - for i in matches: - section_key = "s" + get_doc_section(i) - if section_key not in res: - res[section_key] = [] - if i.type_id=='draft': - if i.get_state_slug()!="rfc": - i.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(i.name) + sections = agenda_sections() + fill_in_agenda_docs(date, sections, docs_by_date[d]) + + for doc in docs_by_date[d]: + if doc.type_id=='draft': + if doc.get_state_slug() != "rfc": + doc.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(doc.name) else: - i.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(i.rfc_number()) - i.iprCount = len(i.ipr()) - res[section_key].append(i) - telechats.append({'date':date, 'docs':res}) + doc.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(doc.rfc_number()) + doc.iprCount = len(doc.ipr()) + + telechats.append({ + "date":date, + "sections": sorted((num, section) for num, section in sections.iteritems() + if "2" <= num < "5") + }) return direct_to_template(request, 'iesg/agenda_documents.html', { 'telechats':telechats }) def telechat_docs_tarfile(request, date): diff --git a/ietf/secr/telechat/views.py b/ietf/secr/telechat/views.py index d8f3b96d4..3413d3c46 100644 --- a/ietf/secr/telechat/views.py +++ b/ietf/secr/telechat/views.py @@ -15,7 +15,7 @@ from ietf.person.models import Person from ietf.doc.lastcall import request_last_call from ietf.doc.mails import email_ad, email_state_changed from ietf.iesg.models import TelechatDate, TelechatAgendaItem -from ietf.iesg.agenda import agenda_data +from ietf.iesg.agenda import agenda_data, get_doc_section from forms import * import os @@ -24,8 +24,6 @@ import datetime ''' EXPECTED CHANGES: x group pages will be just another doc, charter doc -x charter docs to discuss will be passed in the 'docs' section of agenda -x expand get_section_header to include section 4 x consolidate views (get rid of get_group_header,group,group_navigate) ''' @@ -49,8 +47,9 @@ def get_doc_list(agenda): Document objects in the order they appear in the agenda sections 1-3. ''' docs = [] - for key in sorted(agenda['docs']): - docs.extend(agenda['docs'][key]) + for num, section in sorted(agenda['sections'].iteritems()): + if "docs" in section: + docs.extend(section["docs"]) return docs @@ -84,41 +83,29 @@ def get_next_telechat_date(): ''' return TelechatDate.objects.filter(date__gte=datetime.date.today()).order_by('date')[0].date -def get_section_header(file,agenda): +def get_section_header(doc, agenda): ''' This function takes a filename and an agenda dictionary and returns the - agenda section header as a string for use in the doc template + agenda section header as a list for use in the doc template ''' - h1 = {'2':'Protocol Actions','3':'Document Actions','4':'Working Group Actions'} - h2a = {'1':'WG Submissions','2':'Individual Submissions','3':'Status Changes'} - h2b = {'1':'WG Submissions','2':'Individual Submissions via AD','3':'Status Changes','4':'IRTF and Independent Submission Stream Documents'} - h2c = {'1':'WG Creation','2':'WG Chartering'} - h3a = {'1':'New Item','2':'Returning Item','3':'For Action'} - h3b = {'1':'Proposed for IETF Review','2':'Proposed for Approval'} - h3c = {'1':'Under Evaluation for IETF Review','2':'Proposed for Approval'} + num = get_doc_section(doc) - doc = Document.objects.get(name=file) + header = [] - for k,v in agenda['docs'].iteritems(): - if doc in v: - section = k - count = '%s of %s' % (v.index(doc) + 1, len(v)) - break + split = num.split(".") - header = [ '%s %s' % (section[1], h1[section[1]]) ] - if section[1] == '2': - header.append('%s.%s %s' % (section[1], section[2], h2a[section[2]])) - elif section[1] == '4': - header.append('%s.%s %s' % (section[1], section[2], h2c[section[2]])) - else: - header.append('%s.%s %s' % (section[1], section[2], h2b[section[2]])) - if section[1] == '4': - if section[2] == '1': - header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3b[section[3]])) - elif section[2] == '2': - header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3c[section[3]])) - else: - header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3a[section[3]])) + for i in xrange(num.count(".")): + parent_num = ".".join(split[:i + 1]) + parent = agenda["sections"].get(parent_num) + if parent: + if "." not in parent_num: + parent_num += "." + header.append(u"%s %s" % (parent_num, parent["title"])) + + section = agenda["sections"][num] + header.append(u"%s %s" % (num, section["title"])) + + count = '%s of %s' % (section["docs"].index(doc) + 1, len(section["docs"])) header.append(count) return header @@ -127,9 +114,9 @@ def get_first_doc(agenda): ''' This function takes an agenda dictionary and returns the first document in the agenda ''' - for k,v in sorted(agenda['docs'].iteritems()): - if v: - return v[0] + for num, section in sorted(agenda['sections'].iteritems()): + if "docs" in section and section["docs"]: + return section["docs"][0] return None @@ -207,7 +194,7 @@ def doc_detail(request, date, name): BallotFormset = formset_factory(BallotForm, extra=0) agenda = agenda_data(request, date=date) - header = get_section_header(name,agenda) if name else '' + header = get_section_header(doc, agenda) # nav button logic doc_list = get_doc_list(agenda) @@ -376,11 +363,10 @@ def minutes(request, date): This view shows a list of documents that were approved since the last telechat ''' # get the telechat previous to selected one - dates = [ t.date for t in TelechatDate.objects.all() ] y,m,d = date.split('-') current = datetime.date(int(y),int(m),int(d)) - index = dates.index(current) - previous = dates[index + 1] + + previous = TelechatDate.objects.filter(date__lt=current).order_by("-date")[0].date events = DocEvent.objects.filter(type='iesg_approved',time__gte=previous,time__lt=current,doc__type='draft') docs = [ e.doc for e in events ] pa_docs = [ d for d in docs if d.intended_std_level.slug not in ('inf','exp','hist') ] @@ -388,6 +374,8 @@ def minutes(request, date): agenda = agenda_data(request, date=date) + # FIXME: this doesn't show other documents + return render_to_response('telechat/minutes.html', { 'agenda': agenda, 'date': date, diff --git a/ietf/secr/templates/telechat/base_telechat.html b/ietf/secr/templates/telechat/base_telechat.html index 02990e27e..a972b3e5f 100644 --- a/ietf/secr/templates/telechat/base_telechat.html +++ b/ietf/secr/templates/telechat/base_telechat.html @@ -12,77 +12,30 @@ {% endblock %} {% block content %} - +{% load ietf_filters %}

              Agenda {{ date }}

              {% if agenda %}
                -
              • 2 Protocol Actions
              • -
              • 2.1 WG Submissions
              • -
              • 2.1.1 New Item
              • - {% with agenda.docs.s211 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 2.1.2 Returning Item
              • - {% with agenda.docs.s212 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 2.1.3 For Action
              • - {% with agenda.docs.s213 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 2.2 Individual Submissions
              • -
              • 2.2.1 New Item
              • - {% with agenda.docs.s221 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 2.2.2 Returning Item
              • - {% with agenda.docs.s222 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 2.2.3 For Action
              • - {% with agenda.docs.s223 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 2.3 Status Changes
              • -
              • 2.3.1 New Item
              • - {% with agenda.docs.s231 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 2.3.2 Returning Item
              • - {% with agenda.docs.s232 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 3 Document Actions
              • -
              • 3.1 WG Submissions
              • -
              • 3.1.1 New Item
              • - {% with agenda.docs.s311 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 3.1.2 Returning Item
              • - {% with agenda.docs.s312 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 3.1.3 For Action
              • - {% with agenda.docs.s313 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 3.2 Individual Submissions via AD
              • -
              • 3.2.1 New Item
              • - {% with agenda.docs.s321 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 3.2.2 Returning Item
              • - {% with agenda.docs.s322 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 3.2.3 For Action
              • - {% with agenda.docs.s323 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 3.3 Status Changes
              • -
              • 3.3.1 New Item
              • - {% with agenda.docs.s331 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 3.3.2 Returning Item
              • - {% with agenda.docs.s332 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 3.4 Independent Submissions via RFC Editor
              • -
              • 3.4.1 New Item
              • - {% with agenda.docs.s341 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 3.4.2 Returning Item
              • - {% with agenda.docs.s342 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 3.4.3 For Action
              • - {% with agenda.docs.s343 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 4 Working Group Actions
              • -
              • 4.1 WG Creation
              • -
              • 4.1.1 Proposed for IETF Review
              • - {% with agenda.docs.s411 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 4.1.2 Proposed for Approval
              • - {% with agenda.docs.s412 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • -
              • 4.2 WG Rechartering
              • -
              • 4.2.1 Under evaluation for IETF Review
              • - {% with agenda.docs.s421 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
              • 4.2.2 Proposed for Approval
              • - {% with agenda.docs.s422 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} + {% for num, section in agenda.sections.iteritems %} + {% if num >= "2" and num < "5" %} +
              • {{ num }} {{ section.title }} + + {% if "docs" in section %} + {% if section.docs %} + + {% else %} +
                None
                + {% endif %} +
              • + {% endif %} + + {% endif %} + {% endfor %}
              {% else %} Please select a telechat to display the agenda. diff --git a/ietf/templates/iesg/agenda.html b/ietf/templates/iesg/agenda.html index e8e0d7b2c..04de8a160 100644 --- a/ietf/templates/iesg/agenda.html +++ b/ietf/templates/iesg/agenda.html @@ -1,113 +1,120 @@ {% extends "base.html" %} -{% comment %} -Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #} -{% endcomment %} {% load ietf_filters %} -{% block title %}IESG Agenda: {{date}}{% endblock %} -{% block morecss %} -.agenda hr { margin-top: 2em; } -.agenda #section1 h3, #section6 h3 { margin-top:0; margin-bottom:0; } -.agenda noh3 { margin-left: 30px; } -.agenda h4 { margin-top: 0; margin-bottom: 0; } -.agenda h5 { font-size: inherit; margin: 1em 0;} -.agenda #section23 p, #section4 p { margin-left:30px; nomargin-top: 0; nomargin-bottom:0; nofont-style:italic;} -.agenda #section1 pre { margin-left: 30px; } -.agenda blockquote { margin-left: 30px; width: 70ex; font-style:italic;} -table.agenda-doc { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width: 95%; } -table.agenda-doc > tbody > tr { vertical-align:top; } -div.agenda-wg { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width: 95%; } -.agenda .stream { padding-left: 0.5em; } -{% endblock morecss %} +{% block title %}IESG Agenda: {{ date }}{% endblock %} {% block pagehead %} {% endblock %} -{% block content %} +{% block morecss %} +.agenda hr { margin-top: 2em; } +.agenda .section.s1 h3 { margin: 0; } +.agenda .section p, .agenda .section pre, .agenda blockquote, .agenda .agenda-doc { margin-left: 2.2em; } +.agenda h4 { margin-top: 0; margin-bottom: 0; } +.agenda h5 { font-size: inherit; margin: 1em 0; } +.agenda blockquote { width: 37.6em; font-style:italic; } +.agenda .agenda-doc { margin-top: 0.5em; margin-bottom: 0.8em; width: 95%; clear: both; } +.agenda .agenda-doc .ballot-icon-column { float: right; padding: 0.5em 1em; } +.agenda .stream { padding-left: 0.5em; } +{% endblock morecss %} +{% block content %}
              -

              IESG Agenda: {{ date}}

              +

              IESG Agenda: {{ date }}

              -

              See also: Documents on Future IESG Telechat Agendas and IESG Discuss Positions.

              +

              See also: Documents on Future +IESG Telechat Agendas and IESG Discuss +Positions.

              -

              1. Administrivia

              -
              +{% for num, section in sections %} +
              -

              1.1 {% if user|has_role:"Area Director,IAB Chair,Secretariat" %}Roll Call{% else %}Roll Call{% endif %}

              +{% if num|sectionlevel == 1 %}

              {{ num }}. {{ section.title|safe }}

              {% endif %} +{% if num|sectionlevel == 2 %}

              {{ num }} {{ section.title|safe }}

              {% endif %} +{% if num|sectionlevel == 3 %}

              {{ num }} {{ section.title|safe }}

              {% endif %} -

              1.2 Bash the Agenda

              - -

              1.3 Approval of the {% if user|has_role:"Area Director,IAB Chair,Secretariat" %}Minutes{%else%}Minutes{%endif%} of Past Telechats

              -

              1.4 List of Remaining Action Items from Last Telechat

              +{% if num == "1.4" %}
              -{{ action_items }}
              +{{ section.text }}
               
              -
              +{% endif %} -
              -{% with "iesg/agenda_doc.html" as doc_template %} -{% with "iesg/agenda_conflict_doc.html" as doc_conflict_template %} -{% include "iesg/agenda_outline_23.html" %} -{% endwith %} -{% endwith %} -
              +{% if num >= "2" and num < "5" %} + {% if num == "2" %} +
              + Reviews should focus on these questions: "Is this document a + reasonable basis on which to build the salient part of the Internet + infrastructure? If not, what changes would make it so?" +
              + {% endif %} -
              -{% with "iesg/agenda_wg.html" as wg_template %} -{% include "iesg/agenda_outline_4.html" %} -{% endwith %} -
              + {% if num == "3.1" or num == "3.2" %} +
              + Reviews should focus on these questions: "Is this document a + reasonable contribution to the area of Internet engineering + which it covers? If not, what changes would make it so?" +
              + {% endif %} + + {% if num == "3.3" %} +
              + Reviews should focus on these questions: "Are the proposed + changes to document status appropriate? Have all requirements + for such a change been met? If not, what changes to the proposal + would make it appropriate?" +
              + {% endif %} -

              5. IAB News We Can Use

              + {% if num == "3.4" %} +
              + The IESG will use RFC 5742 responses: 1) The IESG has concluded + that there is no conflict between this document and IETF work; 2) + The IESG has concluded that this work is related to IETF work done + in WG <X>, but this relationship does not prevent + publishing; 3) The IESG has concluded that publication could + potentially disrupt the IETF work done in WG <X> and + recommends not publishing the document at this time; 4) The IESG + has concluded that this document violates IETF procedures for + <Y> and should therefore not be published without IETF + review and IESG approval; or 5) The IESG has concluded that this + document extends an IETF protocol in a way that requires IETF + review and should therefore not be published without IETF review + and IESG approval.
              +
              + The document shepherd must propose one of these responses in the + conflict-review document, and the document shepherd may supply text + for an IESG Note in that document. The Area Director ballot positions + indicate consensus with the response proposed by the document shepherd + and agreement that the IESG should request inclusion of the IESG Note.
              +
              + Other matters may be recorded in comments, and the comments will + be passed on to the RFC Editor as community review of the document. +
              + {% endif %} -

              6. Management Issues

              -
              -{% for m in mgmt %} -

              6.{{forloop.counter}} {{m.title|escape}}

              -{% if user|has_role:"Area Director,IAB Chair,Secretariat" %} + + {% if "docs" in section %} + {% for doc in section.docs %} + {% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/agenda_doc.html" %}{% endif %} + {% if doc.type_id == "conflrev" %}{% include "iesg/agenda_conflict_doc.html" %}{% endif %} + {% if doc.type_id == "charter" %}{% include "iesg/agenda_charter.html" %}{% endif %} + {% empty %} +

              NONE

              + {% endfor %} + {% endif %} +{% endif %} + +{% if num|startswith:"6." and user|has_role:"Area Director,IAB Chair,Secretariat" %}
              -{{m.text|wordwrap:"76"}}
              +{{ section.text|wordwrap:"76" }}
               
              -{% endif %}{# if user|has_role #} +{% endif %} +
              {% endfor %} -
              - -

              7. Working Group News

              - -
              {% endblock content %} diff --git a/ietf/templates/iesg/agenda.txt b/ietf/templates/iesg/agenda.txt index 72d302660..e6aa31a6e 100644 --- a/ietf/templates/iesg/agenda.txt +++ b/ietf/templates/iesg/agenda.txt @@ -1,68 +1,24 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #} -{% endcomment %}{% autoescape off %}{% load ietf_filters %}{% filter compress_empty_lines %}{% filter linebreaks_lf %} +{% autoescape off %}{% load ietf_filters %}{% filter compress_empty_lines %}{% filter linebreaks_lf %} INTERNET ENGINEERING STEERING GROUP (IESG) -Summarized Agenda for the {{date}} IESG Teleconference +Summarized Agenda for the {{ date }} IESG Teleconference This agenda was generated at {% now "Y-m-d H:i:s T" %} Up-to-date web version of this agenda can be found at: http://datatracker.ietf.org/iesg/agenda/ - -1. Administrivia - -1.1 Roll Call -1.2 Bash the Agenda -1.3 Approval of the Minutes of Past Telechats -1.4 List of Remaining Action Items from Last Telechat - {{ action_items|indent|indent }} +{% for num, section in sections %} +{% if num|sectionlevel == 1 %} +{{ num }}.{% else %}{{ num }}{% endif %} {{ section.title }}{% if num == "1.4" %} -{% with "iesg/agenda_doc.txt" as doc_template %} -{% with "iesg/agenda_conflict_doc.txt" as doc_conflict_template %} -{% include "iesg/agenda_outline_23.html" %} -{% endwith %} -{% endwith %} + {{ section.text|indent:4 }} +{% endif %}{% if num >= "2" and num < "5" and "docs" in section %}{% for doc in section.docs %} + {% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/agenda_doc.txt" %}{% endif %} + {% if doc.type_id == "conflrev" %}{% include "iesg/agenda_conflict_doc.txt" %}{% endif %} + {% if doc.type_id == "charter" %}{% include "iesg/agenda_charter.txt" %}{% endif %} +{% empty %} -{% with "iesg/agenda_wg.txt" as wg_template %} -{% include "iesg/agenda_outline_4.html" %} -{% endwith %} - -5. IAB News We Can Use - -6. Management Issues -{% for m in mgmt %} -6.{{forloop.counter}} {{m.title}} + NONE {% endfor %} - -7. Working Group News +{% endif %}{% if num|startswith:"6"%} +{% endif %}{% endfor %} {% endfilter %}{% endfilter %}{% endautoescape %} diff --git a/ietf/templates/iesg/agenda_charter.html b/ietf/templates/iesg/agenda_charter.html new file mode 100644 index 000000000..a717c2e49 --- /dev/null +++ b/ietf/templates/iesg/agenda_charter.html @@ -0,0 +1,17 @@ +{% load ballot_icon %} +
              +
              + {% ballot_icon doc %} +
              + +
              + + +
              {{ doc.group.name|escape }} ({{doc.group.acronym}})
              + +
              Area: {{ doc.group.parent.acronym|upper }} ({{ doc.ad|default:"Sponsoring AD not assigned" }})
              +
              +
              diff --git a/ietf/templates/iesg/agenda_charter.txt b/ietf/templates/iesg/agenda_charter.txt new file mode 100644 index 000000000..ec24fb30e --- /dev/null +++ b/ietf/templates/iesg/agenda_charter.txt @@ -0,0 +1,2 @@ +{% load ietf_filters %} + o {{ doc.group.name }} ({{ doc.group.acronym }}) diff --git a/ietf/templates/iesg/agenda_conflict_doc.html b/ietf/templates/iesg/agenda_conflict_doc.html index 7d92fee97..0953039b5 100644 --- a/ietf/templates/iesg/agenda_conflict_doc.html +++ b/ietf/templates/iesg/agenda_conflict_doc.html @@ -1,112 +1,46 @@ -{% comment %} -Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen +{% load ietf_filters ballot_icon %} +
              +
              + {% ballot_icon doc %} +
              -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +
              + {{doc.name}}-{{doc.rev}} + [txt] - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +
              {{ doc.title|escape }} - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. + {% with doc.conflictdoc as conflictdoc %} - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. +
              + {{ conflictdoc.name }}-{{ conflictdoc.rev }} + [txt] +
              {{ conflictdoc.title|escape }} ({{ conflictdoc.stream }}: {{ conflictdoc.intended_std_level }}) + {% if conflictdoc.note %} +
              Note: {{ conflictdoc.note|linebreaksbr }} + {% endif %} -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %}{% load ballot_icon %} - -{% if title2_first %}{% if title1_first %}

              {{ title1 }}

              -{% endif %} -

              {{ title2 }}

              -{% if title2|startswith:"3.4" %} -
              - The IESG will use RFC 5742 responses: 1) The IESG has concluded - that there is no conflict between this document and IETF work; 2) - The IESG has concluded that this work is related to IETF work done - in WG <X>, but this relationship does not prevent - publishing; 3) The IESG has concluded that publication could - potentially disrupt the IETF work done in WG <X> and - recommends not publishing the document at this time; 4) The IESG - has concluded that this document violates IETF procedures for - <Y> and should therefore not be published without IETF - review and IESG approval; or 5) The IESG has concluded that this - document extends an IETF protocol in a way that requires IETF - review and should therefore not be published without IETF review - and IESG approval.
              + {% if conflictdoc.ipr %}
              - The document shepherd must propose one of these responses in the - conflict-review document, and the document shepherd may supply text - for an IESG Note in that document. The Area Director ballot positions - indicate consensus with the response proposed by the document shepherd - and agreement that the IESG should request inclusion of the IESG Note.
              -
              - Other matters may be recorded in comments, and the comments will - be passed on to the RFC Editor as community review of the document. -
              -{% endif %} -{% endif %}

              {{ title3 }}

              +
              IPR:
              + -{% for doc in section_docs %} - - -
              -{{doc.name}}-{{doc.rev}} -[txt] + {% endif %} + -
              {{ doc.title|escape }} -
              -{{doc.conflictdoc.name}}-{{doc.conflictdoc.rev}} -[txt] -
              {{ doc.conflictdoc.title|escape }} ({{doc.conflictdoc.stream}}: {{ doc.conflictdoc.intended_std_level }}) -{% if doc.conflictdoc.note %} -
              Note: {{ doc.conflictdoc.note|linebreaksbr }} -{% endif %} + {% endwith %} -{% if doc.conflictdoc.ipr %} -
              -
              IPR:
              -
                - {% for ipr in doc.conflictdoc.ipr %} - {% ifequal ipr.ipr.status 1 %} -
              • {{ ipr.ipr.title|escape }}
              • - {% endifequal %} - {% endfor %} -
              - -{% endif %} + Token: {{ doc.ad }} + {% with doc.active_defer_event as defer %} + {% if defer %} +
              Was deferred by {{ defer.by }} on {{ defer.time|date:"Y-m-d" }} + {% endif %} + {% endwith %} +
              - -Token: {{ doc.ad }} -{% with doc.active_defer_event as defer %} -{% if defer %} -
              Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}} -{% endif %} -{% endwith %} -
              -{% ballot_icon doc %} -
              - -{% empty %} -

              NONE

              -{% endfor %} diff --git a/ietf/templates/iesg/agenda_conflict_doc.txt b/ietf/templates/iesg/agenda_conflict_doc.txt index e7363db6c..4ff2e3b00 100644 --- a/ietf/templates/iesg/agenda_conflict_doc.txt +++ b/ietf/templates/iesg/agenda_conflict_doc.txt @@ -1,51 +1,8 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} -{% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %} -{{ title2 }} -{% endif %}{{ title3 }} -{% for doc in section_docs %} - o {{doc.canonical_name}}-{{doc.rev}} +{% load ietf_filters %}{% with doc.conflictdoc as conflictdoc %} + o {{ doc.canonical_name }}-{{ doc.rev }} {% filter wordwrap:"68"|indent|indent %}{{ doc.title }}{% endfilter %} - {{doc.conflictdoc.canonical_name}}-{{doc.conflictdoc.rev}} - {% filter wordwrap:"66"|indent:"4" %}{{ doc.conflictdoc.title }} ({{doc.conflictdoc.stream}}: {{ doc.conflictdoc.intended_std_level }}){% endfilter %} -{% if doc.conflictdoc.note %}{# note: note is not escaped #} {% filter wordwrap:"64"|indent:"6" %}Note: {{ doc.conflictdoc.note|striptags }}{% endfilter %} + {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }} + {% filter wordwrap:"66"|indent:"4" %}{{ conflictdoc.title }} ({{ conflictdoc.stream }}: {{ conflictdoc.intended_std_level }}){% endfilter %} +{% if conflictdoc.note %}{# note: note is not escaped #} {% filter wordwrap:"64"|indent:"6" %}Note: {{ conflictdoc.note|striptags }}{% endfilter %} {% endif %} Token: {{ doc.ad }} -{% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %} -{% empty %} - NONE -{% endfor %} +{% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %}{% endwith %} diff --git a/ietf/templates/iesg/agenda_doc.html b/ietf/templates/iesg/agenda_doc.html index 9c7ae80ab..9bc4f3143 100644 --- a/ietf/templates/iesg/agenda_doc.html +++ b/ietf/templates/iesg/agenda_doc.html @@ -1,126 +1,60 @@ -{% comment %} -Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen +{% load ietf_filters ballot_icon %} +
              +
              + {% ballot_icon doc %} +
              -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +
              + {{ doc.canonical_name }} + {% with doc.rfc_number as rfc_number %} + {% if rfc_number %} + [txt] + {% else %} + [txt] + {% endif %} + {% endwith %} - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + {% if doc.stream %} - {{ doc.stream }} stream{% endif %} - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %}{% load ballot_icon %} - -{% if title2_first %}{% if title1_first %}

              {{ title1 }}

              -{% if title1|startswith:"2." %} -
              - Reviews should focus on these questions: "Is this document a - reasonable basis on which to build the salient part of the Internet - infrastructure? If not, what changes would make it so?" -
              -{% endif %} -{% endif %} -

              {{ title2 }}

              -{% if title2|startswith:"3.1" or title2|startswith:"3.2" %} -
              - Reviews should focus on these questions: "Is this document a - reasonable contribution to the area of Internet engineering - which it covers? If not, what changes would make it so?" -
              -{% endif %} -{% if title2|startswith:"3.3" %} -
              - Reviews should focus on these questions: "Are the proposed - changes to document status appropriate? Have all requirements - for such a change been met? If not, what changes to the proposal - would make it appropriate?" -
              -{% endif %} -{% endif %}

              {{ title3 }}

              - -{% for doc in section_docs %} - - -
              -{{doc.canonical_name}} -{% with doc.rfc_number as rfc_number %} -{% if rfc_number %} -[txt] -{% else %} -[txt] -{% endif %} -{% endwith %} - -{% if doc.stream %} - {{ doc.stream }} stream{% endif %} - -
              {{ doc.title|escape }} ({{ doc.intended_std_level }}) +
              {{ doc.title|escape }} ({{ doc.intended_std_level }}) -{% if doc.note %} -
              Note: {{ doc.note|linebreaksbr }} -{% endif %} + {% if doc.note %} +
              Note: {{ doc.note|linebreaksbr }} + {% endif %} -{% if doc.ipr %} -
              -
              IPR:
              - + {% if doc.ipr %} +
              +
              IPR:
              + -{% endif %} + {% endif %} -
              Token: {{ doc.ad }} ({{doc.area_acronym}} area) -{% with doc.active_defer_event as defer %} -{% if defer %} -
              Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}} -{% endif %} -{% endwith %} +
              Token: {{ doc.ad }} ({{ doc.area_acronym }} area) -{% if doc.iana_review_state %} -
              IANA Review: {{ doc.iana_review_state }} -{% endif %} + {% with doc.active_defer_event as defer %} + {% if defer %} +
              Was deferred by {{ defer.by }} on {{ defer.time|date:"Y-m-d" }} + {% endif %} + {% endwith %} -{% if doc.consensus %} -
              Consensus: {{ doc.consensus }} -{% endif %} + {% if doc.iana_review_state %} +
              IANA Review: {{ doc.iana_review_state }} + {% endif %} -{% if doc.lastcall_expires %} -
              Last call expires: {{ doc.lastcall_expires|date:"Y-m-d" }} -{% endif %} + {% if doc.consensus %} +
              Consensus: {{ doc.consensus }} + {% endif %} -
              -{% ballot_icon doc %} -
              + {% if doc.lastcall_expires %} +
              Last call expires: {{ doc.lastcall_expires|date:"Y-m-d" }} + {% endif %} -{% empty %} -

              NONE

              -{% endfor %} +
              +
              diff --git a/ietf/templates/iesg/agenda_doc.txt b/ietf/templates/iesg/agenda_doc.txt index e6537c9dd..2bff27169 100644 --- a/ietf/templates/iesg/agenda_doc.txt +++ b/ietf/templates/iesg/agenda_doc.txt @@ -1,44 +1,4 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} -{% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %} -{{ title2 }} -{% endif %}{{ title3 }} -{% for doc in section_docs %}{% with doc.rfc_number as rfc_number %} +{% load ietf_filters %}{% with doc.rfc_number as rfc_number %} o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %} {% filter wordwrap:"68"|indent|indent %}{{ doc.title }} ({{ doc.intended_std_level }}){% endfilter %} {% if doc.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.note|striptags }}{% endfilter %} @@ -47,6 +7,3 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. Consensus: {{ doc.consensus }}{% endif %}{% if doc.lastcall_expires %} Last call expires: {{ doc.lastcall_expires|date:"Y-m-d" }}{% endif %} {% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %} -{% empty %} - NONE -{% endfor %} diff --git a/ietf/templates/iesg/agenda_documents.html b/ietf/templates/iesg/agenda_documents.html index e04827f12..ae040850e 100644 --- a/ietf/templates/iesg/agenda_documents.html +++ b/ietf/templates/iesg/agenda_documents.html @@ -68,96 +68,28 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% for t in telechats %} -{% if not forloop.first %} - -{% endif %} + {% if not forloop.first %} + + {% endif %} - + -{% if forloop.first %} - -{% endif %} + {% if forloop.first %} + + {% endif %} - + - - - - - {% for doc in t.docs.s211 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s212 %}{% endif %} - {% for doc in t.docs.s212 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s213 %}{% endif %} - {% for doc in t.docs.s213 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - - - {% for doc in t.docs.s221 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s222 %}{% endif %} - {% for doc in t.docs.s222 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s223 %}{% endif %} - {% for doc in t.docs.s223 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - - - {% for doc in t.docs.s231 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s222 %}{% endif %} - {% for doc in t.docs.s232 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s223 %}{% endif %} - {% for doc in t.docs.s233 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - - - - - {% for doc in t.docs.s311 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s312 %}{% endif %} - {% for doc in t.docs.s312 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s313 %}{% endif %} - {% for doc in t.docs.s313 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - - - {% for doc in t.docs.s321 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s322 %}{% endif %} - {% for doc in t.docs.s322 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s323 %}{% endif %} - {% for doc in t.docs.s323 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - - - {% for doc in t.docs.s331 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s332 %}{% endif %} - {% for doc in t.docs.s332 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s333 %}{% endif %} - {% for doc in t.docs.s333 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - - - {% for doc in t.docs.s341 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s342 %}{% endif %} - {% for doc in t.docs.s342 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s343 %}{% endif %} - {% for doc in t.docs.s343 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - - - {% if t.docs.s411 or t.docs.s412%}{% endif %} - - {% if t.docs.s411 %}{% endif %} - {% for doc in t.docs.s411 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s412 %}{% endif %} - {% for doc in t.docs.s412 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - - {% if t.docs.s421 or t.docs.s422 %}{% endif %} - - {% if t.docs.s421 %}{% endif %} - {% for doc in t.docs.s421 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} - {% if t.docs.s422 %}{% endif %} - {% for doc in t.docs.s422 %}{% include "iesg/agenda_documents_row.html" %}{%endfor%} + {% for num, section in t.sections %} + {% if "docs" not in section or section.docs %} + + {% endif %} + {% if "docs" in section and section.docs %} + {% for doc in section.docs %}{% include "iesg/agenda_documents_row.html" %}{% endfor %} + {% endif %} + {% endfor %} {% endfor %} -
               
               
              IESG telechat {{t.date}}
              IESG telechat {{t.date}}
              Full IESG Agenda
              Full IESG Agenda
              Download Documents
              Download Documents
              2. Protocol Actions
              2.1 WG Submissions
              2.1.2 Returning Item
              2.1.3 For Action
              2.2 Individual Submissions
              2.2.2 Returning Item
              2.2.3 For Action
              2.3 Status Changes
              2.3.2 Returning Item
              2.3.3 For Action
              3. Document Actions
              3.1 WG Submissions
              3.1.2 Returning Item
              3.1.3 For Action
              3.2 Individual Submissions Via AD
              3.2.2 Returning Item
              3.2.3 For Action
              3.3 Status Changes
              3.3.2 Returning Item
              3.3.3 For Action
              3.4 IRTF and Independent Submission Stream Documents
              3.4.2 Returning Item
              3.4.3 For Action
              4. Working Group Actions
              4.1 WG Creation
              4.1.1 Proposed for IETF Review
              4.1.2 Proposed for Approval
              4.2 WG Rechartering
              4.2.1 Under Evaluation for IETF Review
              4.2.2 Proposed for Approval
              {{ num }}{% if num|sectionlevel == 1 %}.{% endif %} {{ section.title }}
              diff --git a/ietf/templates/iesg/agenda_outline_23.html b/ietf/templates/iesg/agenda_outline_23.html deleted file mode 100644 index 9311c9e25..000000000 --- a/ietf/templates/iesg/agenda_outline_23.html +++ /dev/null @@ -1,183 +0,0 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% with "2. Protocol Actions" as title1 %}{% with 1 as title1_first %} -{% with "2.1 WG Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "2.1.1 New Items" as title3 %} -{% with docs.s211 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "2.1.2 Returning Items" as title3 %} -{% with docs.s212 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s213 %} -{% with "2.1.3 For Action" as title3 %} -{% with docs.s213 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} -{% endwith %}{# title1_first #} - -{% with "2.2 Individual Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "2.2.1 New Items" as title3 %} -{% with docs.s221 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "2.2.2 Returning Items" as title3 %} -{% with docs.s222 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s223 %} -{% with "2.2.3 For Action" as title3 %} -{% with docs.s223 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% with "2.3 Status Changes" as title2 %} -{% with 1 as title2_first %} - -{% with "2.3.1 New Items" as title3 %} -{% with docs.s231 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "2.3.2 Returning Items" as title3 %} -{% with docs.s232 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s233 %} -{% with "2.3.3 For Action" as title3 %} -{% with docs.s233 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %}{# title1 #} - - -{% with "3. Document Actions" as title1 %}{% with 1 as title1_first %} - -{% with "3.1 WG Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "3.1.1 New Items" as title3 %} -{% with docs.s311 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.1.2 Returning Items" as title3 %} -{% with docs.s312 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s313 %} -{% with "3.1.3 For Action" as title3 %} -{% with docs.s313 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %}{# title1_first #} - -{% with "3.2 Individual Submissions Via AD" as title2 %} -{% with 1 as title2_first %} - -{% with "3.2.1 New Items" as title3 %} -{% with docs.s321 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.2.2 Returning Items" as title3 %} -{% with docs.s322 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s323 %} -{% with "3.2.3 For Action" as title3 %} -{% with docs.s323 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% with "3.3 Status Changes" as title2 %} -{% with 1 as title2_first %} - -{% with "3.3.1 New Items" as title3 %} -{% with docs.s331 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.3.2 Returning Items" as title3 %} -{% with docs.s332 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s333 %} -{% with "3.3.3 For Action" as title3 %} -{% with docs.s333 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %} {# title2 #} - -{% with "3.4 IRTF and Independent Submission Stream Documents" as title2 %} -{% with 1 as title2_first %} - -{% with "3.4.1 New Items" as title3 %} -{% with docs.s341 as section_docs %}{% include doc_conflict_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.4.2 Returning Items" as title3 %} -{% with docs.s342 as section_docs %}{% include doc_conflict_template %}{% endwith %} -{% endwith %} - -{% if docs.s343 %} -{% with "3.4.3 For Action" as title3 %} -{% with docs.s343 as section_docs %}{% include doc_conflict_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %} diff --git a/ietf/templates/iesg/agenda_outline_4.html b/ietf/templates/iesg/agenda_outline_4.html deleted file mode 100644 index 196fa0c86..000000000 --- a/ietf/templates/iesg/agenda_outline_4.html +++ /dev/null @@ -1,63 +0,0 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% with "4. Working Group Actions" as title1 %}{% with 1 as title1_first %} -{% with "4.1 WG Creation" as title2 %} -{% with 1 as title2_first %} - -{% with "4.1.1 Proposed for IETF Review" as title3 %} -{% with wgs.s411 as section_wgs %}{% include wg_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "4.1.2 Proposed for Approval" as title3 %} -{% with wgs.s412 as section_wgs %}{% include wg_template %}{% endwith %} -{% endwith %} - -{% endwith %}{# title2 #} -{% endwith %}{# title1_first #} - -{% with "4.2 WG Rechartering" as title2 %} -{% with 1 as title2_first %} - -{% with "4.2.1 Under Evaluation for IETF Review" as title3 %} -{% with wgs.s421 as section_wgs %}{% include wg_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "4.2.2 Proposed for Approval" as title3 %} -{% with wgs.s422 as section_wgs %}{% include wg_template %}{% endwith %} -{% endwith %} - -{% endwith %}{# title2 #} -{% endwith %}{# title1 #} diff --git a/ietf/templates/iesg/agenda_package.txt b/ietf/templates/iesg/agenda_package.txt index dcf8073c6..bee2be00f 100644 --- a/ietf/templates/iesg/agenda_package.txt +++ b/ietf/templates/iesg/agenda_package.txt @@ -24,10 +24,10 @@ Contents: ------------------------------------------------------------------------ 3. MANAGEMENT ITEM DETAILS ------------------------------------------------------------------------ -{% for m in mgmt %} -6.{{forloop.counter}} {{m.title}} +{% for num, section in management_items %} +{{ num }} {{ section.title}} -{{m.text|wordwrap:"76"}} +{{ section.text|wordwrap:"76" }} {% endfor %} ------------------------------------------------------------------------ diff --git a/ietf/templates/iesg/agenda_wg.html b/ietf/templates/iesg/agenda_wg.html deleted file mode 100644 index 4494ff8e9..000000000 --- a/ietf/templates/iesg/agenda_wg.html +++ /dev/null @@ -1,55 +0,0 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% load ballot_icon %} -{% if title2_first %}{% if title1_first %}

              {{ title1 }}

              -{% endif %} -

              {{ title2 }}

              -{% endif %}

              {{ title3 }}

              - -{% for doc in section_wgs %} - -
              - -{% ballot_icon doc %} - - - -
              {{ doc.group.name|escape }} ({{doc.group.acronym}})
              -
              Area: {{ doc.group.parent.acronym|upper }} ({{ doc.ad|default:"Sponsoring AD not assigned" }})
              -
              -
              - -{% empty %} -

              NONE

              -{% endfor %} diff --git a/ietf/templates/iesg/agenda_wg.txt b/ietf/templates/iesg/agenda_wg.txt deleted file mode 100644 index c1b093afe..000000000 --- a/ietf/templates/iesg/agenda_wg.txt +++ /dev/null @@ -1,42 +0,0 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %}{% autoescape off %}{% load ietf_filters %} -{% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %} -{{ title2 }} -{% endif %}{{ title3 }} -{% for doc in section_wgs %} - o {{ doc.group.name }} ({{ doc.group.acronym }}) -{% empty %} - NONE -{% endfor %} -{% endautoescape %} diff --git a/ietf/templates/iesg/moderator_wg.html b/ietf/templates/iesg/moderator_charter.html similarity index 89% rename from ietf/templates/iesg/moderator_wg.html rename to ietf/templates/iesg/moderator_charter.html index f12a76e92..37b86189f 100644 --- a/ietf/templates/iesg/moderator_wg.html +++ b/ietf/templates/iesg/moderator_charter.html @@ -31,14 +31,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% endcomment %}{% load ietf_filters %} -{% for doc in section_wgs %} -

              {{ title1 }}
              -{{ title2 }}
              -{{ title3 }} ({{ forloop.counter }} of {{ section_wgs|length }})

              +

              {{ doc.group.name }} ({{ doc.group.acronym }})

              -

              {{ doc.group.name }} ({{ doc.group.acronym }})
              - -{% if title3|startswith:"4.1.1" %} +{% if num|startswith:"4.1.1" %}

              Does anyone have an objection to the charter being sent for EXTERNAL REVIEW?

              @@ -52,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OR Back on the Agenda next time in the same category.

              {% endif %} -{% if title3|startswith:"4.1.2" %} +{% if num|startswith:"4.1.2" %}

              Does anyone have an objection to the creation of this working group?

              @@ -64,7 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OR Wait for instructions from the shepherding AD.

              {% endif %} -{% if title3|startswith:"4.2.1" %} +{% if num|startswith:"4.2.1" %}

              Does anyone have an objection with just making the changes to the charter?

              @@ -82,7 +77,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. acronym].

              {% endif %} -{% if title3|startswith:"4.2.2" %} +{% if num|startswith:"4.2.2" %}

              Does anyone have an objection to the rechartering of this working group?

              @@ -94,11 +89,3 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Place the charter back on the agenda for the next telechat.
              * OR Wait for instructions from the shepherding AD.

              {% endif %} - -{% empty %} -

              {{ title1 }}
              -{{ title2 }}
              -{{ title3 }}

              - -

              NONE

              -{% endfor %} diff --git a/ietf/templates/iesg/moderator_conflict_doc.html b/ietf/templates/iesg/moderator_conflict_doc.html index 92daad515..0b32bdd80 100644 --- a/ietf/templates/iesg/moderator_conflict_doc.html +++ b/ietf/templates/iesg/moderator_conflict_doc.html @@ -35,28 +35,33 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endcomment %} {% load ietf_filters %} -{% for doc in section_docs %} -

              {{ title1 }}
              -{{ title2 }}
              -{{ title3 }} ({{ forloop.counter }} of {{ section_docs|length }})

              -
              {{doc.name}}-{{doc.rev}}
              -({{ doc.title|escape }})
              -
              -{{doc.conflictdoc.name}}-{{doc.conflictdoc.rev}}
              -({{ doc.conflictdoc.title|escape }})
              -Intended status: {{ doc.conflictdoc.intended_std_level }}
              +
              + {{ doc.name }}-{{doc.rev}}
              + ({{ doc.title }})
              -
              Token: {{ doc.ad.plain_name|escape }}
              -{% if doc.type.slug == "draft" %} -Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} -{% if doc.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} -{% endif %}
              -{% if doc.active_ballot %} +
              + {{ doc.conflictdoc.name }}-{{ doc.conflictdoc.rev }}
              + ({{ doc.conflictdoc.title }})
              + Intended status: {{ doc.conflictdoc.intended_std_level }}
              +
              + +
              + + Token: {{ doc.ad.plain_name }}
              + {% if doc.type.slug == "draft" %} + Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} + {% if doc.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} + {% endif %} +
              +
              + +{% with doc.active_ballot as ballot %} +{% if ballot %}
                                       Yes  No-Objection  Discuss  Abstain  Recuse
              -{% for pos in doc.active_ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
              +{% for pos in ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
               {% endfor %}
               
              @@ -66,11 +71,12 @@ Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} {% else %}

              (Ballot not issued)

              {% endif %} +{% endwith %} -{% if title3|startswith:"3.4.1" or title3|startswith:"3.4.2" %} +{% if num|startswith:"3.4.1" or num|startswith:"3.4.2" %}

              Does anyone have an objection to the this conflict review response being sent to the {{doc.conflictdoc.stream}}?

              {% endif %} -{% if title3|startswith:"3.4.3" %} +{% if num|startswith:"3.4.3" %}

              Who will do the review of this document?

              {% endif %} @@ -78,7 +84,7 @@ Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} Next State:
              Sub State:

              -{% if title3|startswith:"3.4.1" or title3|startswith:"3.4.2" %} +{% if num|startswith:"3.4.1" or num|startswith:"3.4.2" %}

              If APPROVED - The Secretariat will send a standard no problem message to the RFC Editor. [Name of AD] will you supply the text for the IESG Note?

              @@ -94,12 +100,3 @@ Sub State:

              the Do Not Publish message to the RFC Editor that includes the note drafted by[Name the AD].

              {% endif %} - - -{% empty %} -

              {{ title1 }}
              -{{ title2 }}
              -{{ title3 }}

              - -

              NONE

              -{% endfor %} diff --git a/ietf/templates/iesg/moderator_doc.html b/ietf/templates/iesg/moderator_doc.html index 3e3171a40..1b2156aa9 100644 --- a/ietf/templates/iesg/moderator_doc.html +++ b/ietf/templates/iesg/moderator_doc.html @@ -35,28 +35,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endcomment %} {% load ietf_filters %} -{% for doc in section_docs %} -

              {{ title1 }}
              -{{ title2 }}
              -{{ title3 }} ({{ forloop.counter }} of {{ section_docs|length }})

              -

              {{doc.name}}-{{doc.rev}}
              -({{ doc.title|escape }})
              -Intended status: {{ doc.intended_std_level }}
              -Token: {{ doc.ad.plain_name|escape }}
              -{% if doc.type.slug == "draft" %} -Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} -{% if doc.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} -{% endif %}

              +

              + {{doc.name}}-{{doc.rev}}
              + ({{ doc.title|escape }})
              + + Intended status: {{ doc.intended_std_level }}
              + Token: {{ doc.ad.plain_name|escape }}
              + {% if doc.type_id == "draft" %} + Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} + {% if doc.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} + {% endif %} +
              +

              -{% if doc.active_ballot %} +{% with doc.active_ballot as ballot %} +{% if ballot %}
                                       Yes  No-Objection  Discuss  Abstain  Recuse
              -{% for pos in doc.active_ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
              +{% for pos in ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
               {% endfor %}
               
              -{% if title1|startswith:"2." %} +{% if num|startswith:"2." %}

              ____ open positions
              [   ] would you like to record a position?

              {% endif %} @@ -67,14 +68,15 @@ Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} {% else %}

              (Ballot not issued)

              {% endif %} +{% endwith %} -{% if title2|startswith:"3.1" or title2|startswith:"3.2" %} +{% if num|startswith:"3.1" or num|startswith:"3.2" %}

              Does anyone have an[y further] objection to this document being published as an {{ doc.intended_std_level }} RFC?

              {% endif %} -{% if title3|startswith:"3.3.1" or title3|startswith:"3.3.2" %} -

              Does anyone have an objection to this conflict review response being sent to the {{doc.conflictdoc.stream}}?

              +{% if num|startswith:"3.3.1" or num|startswith:"3.3.2" %} +

              Does anyone have an objection to this conflict review response being sent to the {{ doc.conflictdoc.stream }}?

              {% endif %} -{% if title3|startswith:"3.3.3" %} +{% if num|startswith:"3.3.3" %}

              Who will do the review of this document?

              {% endif %} @@ -82,7 +84,7 @@ Last call ends: {{ doc.most_recent_ietflc.expires.date|default:"(none)" }} Next State:
              Sub State:

              -{% if title3|startswith:"2.1.1" or title3|startswith:"2.1.2" %} +{% if num|startswith:"2.1.1" or num|startswith:"2.1.2" %}

              If APPROVED - The Secretariat will send a working group submission, Protocol Action Announcement.

              @@ -92,7 +94,7 @@ Sub State:

              AD].

              {% endif %} -{% if title3|startswith:"2.2.1" or title3|startswith:"2.2.2" %} +{% if num|startswith:"2.2.1" or num|startswith:"2.2.2" %}

              If APPROVED - The Secretariat will send an individual submission, Protocol Action Announcement.

              @@ -102,7 +104,7 @@ Sub State:

              AD].

              {% endif %} -{% if title3|startswith:"2.3.1" or title3|startswith:"2.3.2" %} +{% if num|startswith:"2.3.1" or num|startswith:"2.3.2" %}

              If APPROVED - The Secretariat will send the associated status change Protocol Action Announcements.

              @@ -112,7 +114,7 @@ Sub State:

              AD].

              {% endif %} -{% if title3|startswith:"3.1.1" or title3|startswith:"3.1.2" %} +{% if num|startswith:"3.1.1" or num|startswith:"3.1.2" %}

              If APPROVED - The Secretariat will send a working group submission Document Action Announcement.

              @@ -121,7 +123,7 @@ Sub State:

              Ed. Note, IESG, note, etc.] from [Name that AD].

              {% endif %} -{% if title3|startswith:"3.2.1" or title3|startswith:"3.2.2" %} +{% if num|startswith:"3.2.1" or num|startswith:"3.2.2" %}

              If APPROVED - The Secretariat will send an individual submission Document Action Announcement.

              @@ -130,7 +132,7 @@ Sub State:

              [RFC Ed. Note, IESG, note, etc.] from [Name that AD].

              {% endif %} -{% if title3|startswith:"3.3.1" or title3|startswith:"3.3.2" %} +{% if num|startswith:"3.3.1" or num|startswith:"3.3.2" %}

              If APPROVED - The Secretariat will send the associated status change Document Action Announcements.

              @@ -139,7 +141,7 @@ Sub State:

              Ed. Note, IESG, note, etc.] from [Name that AD].

              {% endif %} -{% if title3|startswith:"3.4.1" or title3|startswith:"3.4.2" %} +{% if num|startswith:"3.4.1" or num|startswith:"3.4.2" %}

              If APPROVED - The Secretariat will send a standard no problem message to the RFC Editor. [Name of AD] will you supply the text for the IESG Note?

              @@ -155,12 +157,3 @@ Sub State:

              the Do Not Publish message to the RFC Editor that includes the note drafted by[Name the AD].

              {% endif %} - - -{% empty %} -

              {{ title1 }}
              -{{ title2 }}
              -{{ title3 }}

              - -

              NONE

              -{% endfor %} diff --git a/ietf/templates/iesg/moderator_package.html b/ietf/templates/iesg/moderator_package.html index 23de0b3cb..2a68cf4ea 100644 --- a/ietf/templates/iesg/moderator_package.html +++ b/ietf/templates/iesg/moderator_package.html @@ -33,46 +33,47 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% endcomment %}{% comment %} Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endcomment %} -{% load ietf_filters %} - -Moderator Package for {{date}} IESG Telechat - - + + + + Moderator Package for {{ date }} IESG Telechat + -

              Moderator Package for {{date}} IESG Telechat

              +{% load ietf_filters %} + +

              Moderator Package for {{ date }} IESG Telechat

              Generated at {% now "Y-m-d H:i:s T" %}.

              - - - - -

              1. Administrivia
              1.1 Roll Call

              +{% for num, section in sections %} +

              {% for parent_num, parent_section in section.parents %}{{ parent_num }}{% if parent_num|sectionlevel == 1 %}.{% endif %} {{ parent_section.title }}
              {% endfor %}{{ num }}{% if num|sectionlevel == 1 %}.{% endif %} {{ section.title }}

              +{% if num == "1.1" %} {% filter linebreaks_crlf %}
              -{{ roll_call }}
              -
              {% endfilter %} - -

              1. Administrivia
              1.2 Bash the Agenda

              +{{ section.text }} +

              +{% endfilter %} +{% endif %} +{% if num == "1.2" %}

              Does anyone want to add anything NEW to the agenda?

              Does anyone have any other changes to the agenda as it stands?

              +{% endif %} -

              1. Administrivia
              1.3 Approval of the Minutes of Past Telechats

              - +{% if num == "1.3" %}

              Does anyone have an objection to the minutes of the __________ IESG Teleconference being approved?

              @@ -82,70 +83,52 @@ teleconference. The Secretariat will post them in the public archive.

              Are there narrative minutes to approve for today?

              {% filter linebreaks_crlf %}
              -{{ minutes }}
              -
              {% endfilter %} - -

              1. Administrivia
              1.4 List of Remaining Action Items from Last Telechat

              +{{ section.text }} +
      +{% endfilter %} +{% endif %} +{% if num == "1.4" %} {% filter linebreaks_crlf %}
      -{{ action_items }}
      -
      {% endfilter %} +{{ section.text }} + +{% endfilter %} +{% endif %} - - - +{% if num >= "2" and num < "5" %} + {% if "doc" in section %} + {% with section.doc as doc %} + {% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/moderator_doc.html" %}{% endif %} + {% if doc.type_id == "conflrev" %}{% include "iesg/moderator_conflict_doc.html" %}{% endif %} + {% if doc.type_id == "charter" %}{% include "iesg/moderator_charter.html" %}{% endif %} + {% endwith %} + {% else %} +

      NONE

      + {% endif %} +{% endif %} -{% with "iesg/moderator_doc.html" as doc_template %} -{% with "iesg/moderator_conflict_doc.html" as doc_conflict_template %} -{% include "iesg/agenda_outline_23.html" %} -{% endwith %} -{% endwith %} +{% if num >= "6" and num < "7" %} + {% if num == "6" %} +

      NONE

      + {% else %} +

      Is there anything that you would like the Secretariat to record in the minutes for this management issue?

      - - - +

      Decision:

      -{% with "iesg/moderator_wg.html" as wg_template %} -{% include "iesg/agenda_outline_4.html" %} -{% endwith %} +

      The management issue was discussed.

      - - - - -

      5. IAB News We Can Use

      - - - - - -{% for m in mgmt %} -

      6. Management Items
      -6.{{forloop.counter}} {{m.title}}

      - -

      Is there anything that you would like the Secretariat to record in the minutes for this management issue?

      - -

      Decision:

      - -

      The management issue was discussed.

      - -

      Action Item (if applicable):

      - -{% empty %} -

      6. Management Items

      - -

      NONE

      -{% endfor %} - - - - - -

      7. Working Group News

      +

      Action Item (if applicable):

      + {% endif %} +{% endif %} +{% if num == "7" %} {% filter linebreaks_crlf %}
      -{% for ad in ads %}[ ] {{ ad.plain_name }}
      +{% for ad in section.ads %}[ ] {{ ad.plain_name }}
       {% endfor %}
       
      {% endfilter %} +{% endif %} - +{% endfor %} + + + diff --git a/ietf/templates/iesg/scribe_conflict_doc.html b/ietf/templates/iesg/scribe_conflict_doc.html index 7d1a9eaad..8508438d4 100644 --- a/ietf/templates/iesg/scribe_conflict_doc.html +++ b/ietf/templates/iesg/scribe_conflict_doc.html @@ -1,98 +1,23 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen +
    • + {{ doc.title }} -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +
      {{doc.canonical_name}} + [txt] - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +
      + {{ doc.conflictdoc.title }} ({{ doc.conflictdoc.stream }}: {{ doc.conflictdoc.intended_std_level }}) +
      {{ doc.conflictdoc.canonical_name }} + [txt] + {% if doc.conflictdoc.note %} +
      Note: {{ doc.conflictdoc.note|linebreaksbr }} + {% endif %} + {% for ipr in doc.conflictdoc.ipr %} + {% if ipr.ipr.status == 1 %} +
      IPR: {{ ipr.ipr.title }} + {% endif %} + {% endfor %} +
      + Token: {{ doc.ad }} - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %}{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} -{% if title2_first %} - {% if title1_first %} -

      {{ title1 }}

      - {% endif %} -

      {{ title2 }}

      - {% endif %}

      {{ title3 }}

      - -
        - {% for doc in section_docs %} -
      • - {{ doc.title|escape }} -
        {{doc.canonical_name}} - [txt] -
        - {{ doc.conflictdoc.title|escape }} ({{doc.conflictdoc.stream}}: {{ doc.conflictdoc.intended_std_level }}) -
        {{doc.conflictdoc.canonical_name}} - [txt] - {% if doc.conflictdoc.note %} -
        Note: {{ doc.conflictdoc.note|linebreaksbr }} - {% endif %} - {% for ipr in doc.conflictdoc.ipr %} - {% ifequal ipr.ipr.status 1 %} -
        IPR: {{ ipr.ipr.title|escape }} - {% endifequal %} - {% endfor %} -
        - Token: {{ doc.ad.plain_name|escape }} - {% if doc.active_ballot %} -
        Discusses/comments [ballot]: - - {% endif %} - -

        Telechat:

        -
          -
        • ...
        • -
        • ...
        • -
        • ...
        • -
        • ...
        • -
        • ...
        • -
        + {% include "iesg/scribe_doc_ballot.html" %}
      • -{% empty %} -
      • (none)
      • -{% endfor %} -
      diff --git a/ietf/templates/iesg/scribe_doc.html b/ietf/templates/iesg/scribe_doc.html index cf2fa54bf..29b76a018 100644 --- a/ietf/templates/iesg/scribe_doc.html +++ b/ietf/templates/iesg/scribe_doc.html @@ -1,99 +1,25 @@ -{% comment %}{% endcomment %}{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} -{% if title2_first %} - {% if title1_first %} -

      {{ title1 }}

      - {% endif %} -

      {{ title2 }}

      - {% endif %}

      {{ title3 }}

      - -
        - {% for doc in section_docs %} -
      • - {{ doc.title|escape }} ({{ doc.intended_std_level }}) -
        {{doc.canonical_name}} - {% with doc.rfc_number as rfc_number %} - {% if rfc_number %} - [txt] - {% else %} - [txt] - {% endif %} - {% endwith %} -
        Token: {{ doc.ad.plain_name|escape }} ({{doc.area_acronym}} area) - {% if doc.note %} -
        Note: {{ doc.note|linebreaksbr }} - {% endif %} - {% for ipr in doc.ipr %} - {% ifequal ipr.ipr.status 1 %} -
        IPR: {{ ipr.ipr.title|escape }} - {% endifequal %} - {% endfor %} - {% if doc.active_ballot %} -
        Discusses/comments [ballot]: - - {% endif %} - -

        Telechat:

        -
          -
        • ...
        • -
        • ...
        • -
        • ...
        • -
        • ...
        • -
        • ...
        • -
        + {% include "iesg/scribe_doc_ballot.html" %}
      • -{% empty %} -
      • (none)
      • -{% endfor %} -
      diff --git a/ietf/templates/iesg/scribe_doc2.html b/ietf/templates/iesg/scribe_doc2.html deleted file mode 100644 index 9297218a6..000000000 --- a/ietf/templates/iesg/scribe_doc2.html +++ /dev/null @@ -1,57 +0,0 @@ -{% comment %}{% endcomment %}{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} - - -

      {{ doc.name }}

      -{% autoescape off %} -{% if doc.active_ballot %} - -{% endif%} -{% endautoescape %} diff --git a/ietf/templates/iesg/scribe_doc_ballot.html b/ietf/templates/iesg/scribe_doc_ballot.html new file mode 100644 index 000000000..f00f67f93 --- /dev/null +++ b/ietf/templates/iesg/scribe_doc_ballot.html @@ -0,0 +1,33 @@ + {% with doc.active_ballot as ballot %} + {% if ballot %} +
      Discusses/comments [ballot]: + + {% endif %} + {% endwith %} + +

      Telechat:

      + +
        +
      • ...
      • +
      • ...
      • +
      • ...
      • +
      • ...
      • +
      • ...
      • +
      \ No newline at end of file diff --git a/ietf/templates/iesg/scribe_template.html b/ietf/templates/iesg/scribe_template.html index 78c97dd1b..980089927 100644 --- a/ietf/templates/iesg/scribe_template.html +++ b/ietf/templates/iesg/scribe_template.html @@ -41,40 +41,50 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% load ietf_filters %}{% filter compress_empty_lines %} -

      Scribe template for IESG Narrative Minutes, {{date}}

      +

      Scribe template for IESG Narrative Minutes, {{ date }}

      -{% with "iesg/scribe_doc.html" as doc_template %} -{% with "iesg/scribe_conflict_doc.html" as doc_conflict_template %} -{% include "iesg/agenda_outline_23.html" %} -{% endwith %} -{% endwith %} +{% for num, section in sections %} +

      {{ num }}{% if num|sectionlevel == 1 %}.{% endif %} {{ section.title|safe }}

      + + {% if "docs" in section %} +
        {% for doc in section.docs %} +{% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/scribe_doc.html" %}{% endif %} +{% if doc.type_id == "conflrev" %}{% include "iesg/scribe_conflict_doc.html" %}{% endif %} + {% empty %} +
      • (none)
      • {% endfor %} +
      + {% endif %} +{% endfor %}

      Appendix: Snapshot of discusses/comments

      (at {% now "Y-m-d H:i:s T" %})

      -{% for doc in docs.s211 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s212 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s213 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s221 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s222 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s223 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s231 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s232 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s233 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s311 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s312 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s313 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s321 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s322 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s323 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s331 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s332 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s333 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s341 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s342 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s343 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} +{% for doc in appendix_docs %} + +

      {{ doc.name }}

      + + {% with doc.active_ballot as ballot %} + {% if ballot %} + + {% endif %} + {% endwith %} + +{% endfor %} {% endfilter %} From d915a3183364f9dc85abba59b02781b706a2703a Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 18 Oct 2013 16:42:30 +0000 Subject: [PATCH 091/173] Add some styling from previous adaptation of Secretariat telechat tool that were missing from the commit - Legacy-Id: 6467 --- static/secretariat/css/custom.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/static/secretariat/css/custom.css b/static/secretariat/css/custom.css index 0205d85ff..07be32d83 100644 --- a/static/secretariat/css/custom.css +++ b/static/secretariat/css/custom.css @@ -661,6 +661,10 @@ ul.session-buttons { list-style-type: circle; } +#telechat-sidebar ul.doc-list { + margin-bottom: 0.8em; +} + ul.doc-list li { list-style-type: circle; } @@ -685,6 +689,18 @@ ul.doc-list li { font-size: 110%; } +#telechat-sidebar li.level3 + li.level2 { + margin-top: 1em; +} + +#telechat-sidebar li.level3 + li.level1 { + margin-top: 1.5em; +} + +#telechat-sidebar li div { + font-style: italic; +} + #telechat-positions-table td { text-align: center; } From 9842eef40588b717cbafa56e69bf60b8f01d0078 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 25 Oct 2013 12:53:54 +0000 Subject: [PATCH 092/173] For no good reason, agenda_data took request as first parameter, remove this as it makes testing harder - Legacy-Id: 6487 --- ietf/iesg/agenda.py | 2 +- ietf/iesg/views.py | 12 ++++++------ ietf/secr/telechat/views.py | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ietf/iesg/agenda.py b/ietf/iesg/agenda.py index 48a57483a..84e737c0e 100644 --- a/ietf/iesg/agenda.py +++ b/ietf/iesg/agenda.py @@ -203,7 +203,7 @@ def fill_in_agenda_management_issues(date, sections): for i, item in enumerate(TelechatAgendaItem.objects.filter(type=3).order_by('id'), start=1): sections[s % i] = { "title": item.title, "text": item.text } -def agenda_data(request, date=None): +def agenda_data(date=None): """Return a dict with the different IESG telechat agenda components.""" date = get_agenda_date(date) sections = agenda_sections() diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 9669b70f6..85796ab40 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -84,7 +84,7 @@ def review_decisions(request, year=None): context_instance=RequestContext(request)) def agenda_json(request, date=None): - data = agenda_data(request, date) + data = agenda_data(date) res = { "telechat-date": str(data["date"]), @@ -171,7 +171,7 @@ def agenda_json(request, date=None): return HttpResponse(json.dumps(res, indent=2), mimetype='text/plain') def agenda(request, date=None): - data = agenda_data(request, date) + data = agenda_data(date) if has_role(request.user, ["Area Director", "IAB Chair", "Secretariat"]): data["sections"]["1.1"]["title"] = data["sections"]["1.1"]["title"].replace("Roll Call", 'Roll Call') @@ -184,14 +184,14 @@ def agenda(request, date=None): }, context_instance=RequestContext(request)) def agenda_txt(request, date=None): - data = agenda_data(request, date) + data = agenda_data(date) return render_to_response("iesg/agenda.txt", { "date": data["date"], "sections": sorted(data["sections"].iteritems()), }, context_instance=RequestContext(request), mimetype="text/plain") def agenda_scribe_template(request, date=None): - data = agenda_data(request, date) + data = agenda_data(date) sections = sorted((num, section) for num, section in data["sections"].iteritems() if "2" <= num < "4") appendix_docs = [] for num, section in sections: @@ -208,7 +208,7 @@ def agenda_scribe_template(request, date=None): def agenda_moderator_package(request, date=None): """Output telechat agenda with one page per section, with each document in its own section.""" - data = agenda_data(request, date) + data = agenda_data(date) def leaf_section(num, section): return not (num == "1" @@ -255,7 +255,7 @@ def agenda_moderator_package(request, date=None): @role_required('Area Director', 'Secretariat') def agenda_package(request, date=None): - data = agenda_data(request, date) + data = agenda_data(date) return render_to_response("iesg/agenda_package.txt", { "date": data["date"], "sections": sorted(data["sections"].iteritems()), diff --git a/ietf/secr/telechat/views.py b/ietf/secr/telechat/views.py index 3413d3c46..f55462a48 100644 --- a/ietf/secr/telechat/views.py +++ b/ietf/secr/telechat/views.py @@ -35,7 +35,7 @@ active_ballot_positions: takes one argument, doc. returns a dictionary with a k NOTE: this function has been deprecated as of Datatracker 4.34. Should now use methods on the Document. For example: doc.active_ballot().active_ad_positions() -agenda_data: takes a request object and a date string in the format YYYY-MM-DD. +agenda_data: takes a date string in the format YYYY-MM-DD. ''' # ------------------------------------------------- @@ -125,7 +125,7 @@ def get_first_doc(agenda): # ------------------------------------------------- def bash(request, date): - agenda = agenda_data(request, date=date) + agenda = agenda_data(date=date) return render_to_response('telechat/bash.html', { 'agenda': agenda, @@ -139,7 +139,7 @@ def doc(request, date): displays the message "No Documents" ''' - agenda = agenda_data(request, date=date) + agenda = agenda_data(date=date) doc = get_first_doc(agenda) if doc: url = reverse('telechat_doc_detail', kwargs={'date':date,'name':doc.name}) @@ -193,7 +193,7 @@ def doc_detail(request, date, name): 'substate':tag} BallotFormset = formset_factory(BallotForm, extra=0) - agenda = agenda_data(request, date=date) + agenda = agenda_data(date=date) header = get_section_header(doc, agenda) # nav button logic @@ -310,7 +310,7 @@ def doc_navigate(request, date, name, nav): The view retrieves the appropriate document and redirects to the doc view. ''' doc = get_object_or_404(Document, docalias__name=name) - agenda = agenda_data(request, date=date) + agenda = agenda_data(date=date) target = name docs = get_doc_list(agenda) @@ -348,7 +348,7 @@ def management(request, date): This view displays management issues and lets the user update the status ''' - agenda = agenda_data(request, date=date) + agenda = agenda_data(date=date) issues = TelechatAgendaItem.objects.filter(type=3).order_by('id') return render_to_response('telechat/management.html', { @@ -372,7 +372,7 @@ def minutes(request, date): pa_docs = [ d for d in docs if d.intended_std_level.slug not in ('inf','exp','hist') ] da_docs = [ d for d in docs if d.intended_std_level.slug in ('inf','exp','hist') ] - agenda = agenda_data(request, date=date) + agenda = agenda_data(date=date) # FIXME: this doesn't show other documents @@ -400,7 +400,7 @@ def new(request): def roll_call(request, date): - agenda = agenda_data(request, date=date) + agenda = agenda_data(date=date) ads = Person.objects.filter(role__name='ad', role__group__state="active") sorted_ads = sorted(ads, key = lambda a: a.name_parts()[3]) From d202c658357b8822fc0532aa4d1e16e436371dab Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 25 Oct 2013 12:54:39 +0000 Subject: [PATCH 093/173] Add tests of document placement in the agenda - Legacy-Id: 6488 --- ietf/iesg/tests.py | 173 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 3cce3b115..2104d8840 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -13,7 +13,7 @@ from ietf.person.models import Person from ietf.group.models import Group from ietf.name.models import StreamName from ietf.iesg.models import * -from ietf.iesg.agenda import get_agenda_date +from ietf.iesg.agenda import get_agenda_date, agenda_data class ReviewDecisionsTests(django.test.TestCase): def test_review_decisions(self): @@ -43,7 +43,7 @@ class IESGAgendaTests(django.test.TestCase): "ietf_draft": Document.objects.get(name="draft-ietf-mars-test"), "ise_draft": ise_draft, "conflrev": Document.objects.get(name="conflict-review-imaginary-irtf-submission"), - "statusch": Document.objects.get(name="status-change-imaginary-mid-review"), + "statchg": Document.objects.get(name="status-change-imaginary-mid-review"), "charter": Document.objects.filter(type="charter")[0], } @@ -65,6 +65,175 @@ class IESGAgendaTests(django.test.TestCase): def tearDown(self): shutil.rmtree(self.draft_dir) + def test_fill_in_agenda_docs(self): + draft = self.telechat_docs["ietf_draft"] + statchg = self.telechat_docs["statchg"] + conflrev = self.telechat_docs["conflrev"] + charter = self.telechat_docs["charter"] + + # put on agenda + date = datetime.date.today() + datetime.timedelta(days=50) + TelechatDate.objects.create(date=date) + telechat_event = TelechatDocEvent.objects.create( + type="scheduled_for_telechat", + doc=draft, + by=Person.objects.get(name="Aread Irector"), + telechat_date=date, + returning_item=False) + date_str = date.isoformat() + + # 2.1 protocol WG submissions + draft.intended_std_level_id = "ps" + draft.group = Group.objects.get(acronym="mars") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.3"]["docs"]) + + # 2.2 protocol individual submissions + draft.group = Group.objects.get(type="individ") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.3"]["docs"]) + + # 3.1 document WG submissions + draft.intended_std_level_id = "inf" + draft.group = Group.objects.get(acronym="mars") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.3"]["docs"]) + + # 3.2 document individual submissions + draft.group = Group.objects.get(type="individ") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.3"]["docs"]) + + + # 2.3 protocol status changes + telechat_event.doc = statchg + telechat_event.save() + + relation = RelatedDocument.objects.create( + source=statchg, + target=DocAlias.objects.filter(name__startswith='rfc', document__std_level="ps")[0], + relationship_id="tohist") + + statchg.group = Group.objects.get(acronym="mars") + statchg.save() + statchg.set_state(State.objects.get(type="statchg", slug="iesgeval")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + statchg.set_state(State.objects.get(type="statchg", slug="adrev")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.3"]["docs"]) + + # 3.3 document status changes + relation.target = DocAlias.objects.filter(name__startswith='rfc', document__std_level="inf")[0] + relation.save() + + statchg.group = Group.objects.get(acronym="mars") + statchg.save() + statchg.set_state(State.objects.get(type="statchg", slug="iesgeval")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + statchg.set_state(State.objects.get(type="statchg", slug="adrev")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.3"]["docs"]) + + + # 3.4 IRTF/ISE conflict reviews + telechat_event.doc = conflrev + telechat_event.save() + + conflrev.group = Group.objects.get(acronym="mars") + conflrev.save() + conflrev.set_state(State.objects.get(type="conflrev", slug="iesgeval")) + self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + conflrev.set_state(State.objects.get(type="conflrev", slug="needshep")) + self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.3"]["docs"]) + + + # 4 WGs + telechat_event.doc = charter + telechat_event.save() + + charter.group = Group.objects.get(acronym="mars") + charter.save() + + charter.group.state_id = "bof" + charter.group.save() + + charter.set_state(State.objects.get(type="charter", slug="infrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.1.1"]["docs"]) + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.1.2"]["docs"]) + + charter.group.state_id = "active" + charter.group.save() + + charter.set_state(State.objects.get(type="charter", slug="infrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.2.1"]["docs"]) + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.2.2"]["docs"]) + + #for n, s in agenda_data(date_str)["sections"].iteritems(): + # print n, s.get("docs") if "docs" in s else s["title"] + def test_feed(self): url = "/feed/iesg-agenda/" From 52583aa8508e0e6bb6cb800a1f9f4c1425fd8d99 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 25 Oct 2013 13:55:49 +0000 Subject: [PATCH 094/173] Add extra newline, forgot to commit that with previous commit - Legacy-Id: 6489 --- ietf/iesg/agenda.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ietf/iesg/agenda.py b/ietf/iesg/agenda.py index 84e737c0e..d9c69ac81 100644 --- a/ietf/iesg/agenda.py +++ b/ietf/iesg/agenda.py @@ -72,6 +72,7 @@ def get_doc_section(doc): s += ".2" else: s += ".1" + elif doc.type_id == 'conflrev': if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'): s = "3.4.3" From a7f515f4b2bbdc4834461e1ab8703d397bde3891 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 4 Nov 2013 11:11:50 +0000 Subject: [PATCH 095/173] Fix link pointing to /idtracker/ - Legacy-Id: 6645 --- ietf/templates/doc/document_draft.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/templates/doc/document_draft.html b/ietf/templates/doc/document_draft.html index 9b1883084..9a8d9aa6c 100644 --- a/ietf/templates/doc/document_draft.html +++ b/ietf/templates/doc/document_draft.html @@ -152,7 +152,7 @@ {% endif %} - IESG State: + IESG State: {{ iesg_state_summary|default:"I-D Exists" }} From e1708da2fe891ce9c073c12ab6fb6d1234adf18e Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 4 Nov 2013 13:49:32 +0000 Subject: [PATCH 096/173] Cosmetic fix, insert missing space - Legacy-Id: 6646 --- ietf/wginfo/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index a81fbf3fd..5d5c12795 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -53,7 +53,7 @@ def roles(group, role_name): def fill_in_charter_info(group, include_drafts=False): group.areadirector = group.ad.role_email("ad", group.parent) if group.ad else None - group.chairs =roles(group, "chair") + group.chairs = roles(group, "chair") group.techadvisors = roles(group, "techadv") group.editors = roles(group, "editor") group.secretaries = roles(group, "secr") From 2daff23f02c358bdc41832d84f83af054010efb8 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 5 Nov 2013 16:59:13 +0000 Subject: [PATCH 097/173] Remove shim layer from submit code - Legacy-Id: 6654 --- ietf/submit/forms.py | 193 ++++++------------- ietf/submit/models.py | 21 +- ietf/submit/parsers/plain_parser.py | 3 - ietf/submit/utils.py | 202 ++++---------------- ietf/submit/views.py | 23 ++- ietf/templates/submit/announce_to_lists.txt | 2 +- 6 files changed, 116 insertions(+), 328 deletions(-) diff --git a/ietf/submit/forms.py b/ietf/submit/forms.py index 1aecf5d35..d99b64294 100644 --- a/ietf/submit/forms.py +++ b/ietf/submit/forms.py @@ -14,11 +14,11 @@ from django.core.urlresolvers import reverse as urlreverse import debug -from ietf.group.models import Group -from ietf.idtracker.models import InternetDraft, IETFWG -from ietf.proceedings.models import Meeting +from ietf.group.models import Group, Role +from ietf.doc.models import Document +from ietf.meeting.models import Meeting from ietf.submit.models import IdSubmissionDetail, TempIdAuthors, Preapproval -from ietf.submit.utils import MANUAL_POST_REQUESTED, NONE_WG, UPLOADED, AWAITING_AUTHENTICATION, POSTED, POSTED_BY_SECRETARIAT +from ietf.submit.utils import MANUAL_POST_REQUESTED, UPLOADED, AWAITING_AUTHENTICATION, POSTED, POSTED_BY_SECRETARIAT, submission_confirmation_email_list from ietf.submit.parsers.pdf_parser import PDFParser from ietf.submit.parsers.plain_parser import PlainParser from ietf.submit.parsers.ps_parser import PSParser @@ -154,14 +154,14 @@ class UploadForm(forms.Form): same_name = IdSubmissionDetail.objects.filter(filename=filename, revision=revision, submission_date=today) if same_name.count() > settings.MAX_SAME_DRAFT_NAME: raise forms.ValidationError('The same I-D cannot be submitted more than %s times a day' % settings.MAX_SAME_DRAFT_NAME) - if sum([i.filesize for i in same_name]) > (settings.MAX_SAME_DRAFT_NAME_SIZE * 1048576): + if sum(i.filesize for i in same_name) > settings.MAX_SAME_DRAFT_NAME_SIZE * 1048576: raise forms.ValidationError('The same I-D submission cannot exceed more than %s MByte a day' % settings.MAX_SAME_DRAFT_NAME_SIZE) # Total from same ip same_ip = IdSubmissionDetail.objects.filter(remote_ip=remote_ip, submission_date=today) if same_ip.count() > settings.MAX_SAME_SUBMITTER: raise forms.ValidationError('The same submitter cannot submit more than %s I-Ds a day' % settings.MAX_SAME_SUBMITTER) - if sum([i.filesize for i in same_ip]) > (settings.MAX_SAME_SUBMITTER_SIZE * 1048576): + if sum(i.filesize for i in same_ip) > settings.MAX_SAME_SUBMITTER_SIZE * 1048576: raise forms.ValidationError('The same submitter cannot exceed more than %s MByte a day' % settings.MAX_SAME_SUBMITTER_SIZE) # Total in same group @@ -169,7 +169,7 @@ class UploadForm(forms.Form): same_group = IdSubmissionDetail.objects.filter(group_acronym=self.group, submission_date=today) if same_group.count() > settings.MAX_SAME_WG_DRAFT: raise forms.ValidationError('The same working group I-Ds cannot be submitted more than %s times a day' % settings.MAX_SAME_WG_DRAFT) - if sum([i.filesize for i in same_group]) > (settings.MAX_SAME_WG_DRAFT_SIZE * 1048576): + if sum(i.filesize for i in same_group) > settings.MAX_SAME_WG_DRAFT_SIZE * 1048576: raise forms.ValidationError('Total size of same working group I-Ds cannot exceed %s MByte a day' % settings.MAX_SAME_WG_DRAFT_SIZE) @@ -177,7 +177,7 @@ class UploadForm(forms.Form): total_today = IdSubmissionDetail.objects.filter(submission_date=today) if total_today.count() > settings.MAX_DAILY_SUBMISSION: raise forms.ValidationError('The total number of today\'s submission has reached the maximum number of submission per day') - if sum([i.filesize for i in total_today]) > (settings.MAX_DAILY_SUBMISSION_SIZE * 1048576): + if sum(i.filesize for i in total_today) > settings.MAX_DAILY_SUBMISSION_SIZE * 1048576: raise forms.ValidationError('The total size of today\'s submission has reached the maximum size of submission per day') def check_paths(self): @@ -234,10 +234,10 @@ class UploadForm(forms.Form): def get_working_group(self): name = self.draft.filename - existing_draft = InternetDraft.objects.filter(filename=name) + existing_draft = Document.objects.filter(name=name, type="draft") if existing_draft: - group = existing_draft[0].group and existing_draft[0].group.ietfwg or None - if group and group.pk != NONE_WG and group.type_id != "area": + group = existing_draft[0].group + if group and group.type_id not in ("individ", "area"): return group else: return None @@ -255,25 +255,18 @@ class UploadForm(forms.Form): # first check groups with dashes for g in Group.objects.filter(acronym__contains="-", type=group_type): if name.startswith('draft-%s-%s-' % (components[1], g.acronym)): - return IETFWG().from_object(g) + return g try: - return IETFWG().from_object(Group.objects.get(acronym=components[2], type=group_type)) + return Group.objects.get(acronym=components[2], type=group_type) except Group.DoesNotExist: raise forms.ValidationError('There is no active group with acronym \'%s\', please rename your draft' % components[2]) elif name.startswith("draft-iab-"): - return IETFWG().from_object(Group.objects.get(acronym="iab")) + return Group.objects.get(acronym="iab") else: return None def save_draft_info(self, draft): - document_id = 0 - existing_draft = InternetDraft.objects.filter(filename=draft.filename) - if existing_draft: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - document_id = -1 - else: - document_id = existing_draft[0].id_document_tag detail = IdSubmissionDetail.objects.create( id_document_name=draft.get_title(), filename=draft.filename, @@ -283,7 +276,7 @@ class UploadForm(forms.Form): creation_date=draft.get_creation_date(), submission_date=datetime.date.today(), idnits_message=self.idnits_message, - temp_id_document_tag=document_id, + temp_id_document_tag=-1, group_acronym=self.group, remote_ip=self.remote_ip, first_two_pages=''.join(draft.pages[:2]), @@ -291,38 +284,21 @@ class UploadForm(forms.Form): abstract=draft.get_abstract(), file_type=','.join(self.file_type), ) - order = 0 - for author in draft.get_author_list(): + for order, author in enumerate(draft.get_author_list(), start=1): full_name, first_name, middle_initial, last_name, name_suffix, email, company = author - order += 1 - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # save full name - TempIdAuthors.objects.create( - id_document_tag=document_id, - first_name=full_name.strip(), - email_address=(email or "").strip(), - author_order=order, - submission=detail) - else: - TempIdAuthors.objects.create( - id_document_tag=document_id, - first_name=first_name, - middle_initial=middle_initial, - last_name=last_name, - name_suffix=name_suffix, - email_address=email, - author_order=order, - submission=detail) + # save full name + TempIdAuthors.objects.create( + id_document_tag=-1, + first_name=full_name.strip(), + email_address=(email or "").strip(), + author_order=order, + submission=detail) return detail class AutoPostForm(forms.Form): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - name = forms.CharField(required=True) - else: - first_name = forms.CharField(label=u'Given name', required=True) - last_name = forms.CharField(label=u'Last name', required=True) + name = forms.CharField(required=True) email = forms.EmailField(label=u'Email address', required=True) def __init__(self, *args, **kwargs): @@ -332,26 +308,12 @@ class AutoPostForm(forms.Form): super(AutoPostForm, self).__init__(*args, **kwargs) def get_author_buttons(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - buttons = [] - for i in self.validation.authors: - buttons.append('' - % dict(name=i.get_full_name(), - email=i.email()[1] or '')) - return "".join(buttons) - - - # this should be moved to a Javascript file and attributes like data-first-name ... - button_template = '' - buttons = [] for i in self.validation.authors: - full_name = u'%s. %s' % (i.first_name[0], i.last_name) - buttons.append(button_template % {'first_name': i.first_name, - 'last_name': i.last_name, - 'email': i.email()[1] or '', - 'full_name': full_name}) - return ''.join(buttons) + buttons.append('' + % dict(name=i.get_full_name(), + email=i.email()[1] or '')) + return "".join(buttons) def save(self, request): self.save_submitter_info() @@ -361,7 +323,7 @@ class AutoPostForm(forms.Form): def send_confirmation_mail(self, request): subject = 'Confirmation for Auto-Post of I-D %s' % self.draft.filename from_email = settings.IDSUBMIT_FROM_EMAIL - to_email = self.draft.confirmation_email_list() + to_email = submission_confirmation_email_list(self.draft) confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_confirm', kwargs=dict(submission_id=self.draft.submission_id, auth_key=self.draft.auth_key)) status_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_status_by_hash', kwargs=dict(submission_id=self.draft.submission_id, submission_hash=self.draft.get_hash())) @@ -370,21 +332,13 @@ class AutoPostForm(forms.Form): { 'draft': self.draft, 'confirm_url': confirm_url, 'status_url': status_url }) def save_submitter_info(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return TempIdAuthors.objects.create( - id_document_tag=self.draft.temp_id_document_tag, - first_name=self.cleaned_data['name'], - email_address=self.cleaned_data['email'], - author_order=0, - submission=self.draft) - return TempIdAuthors.objects.create( id_document_tag=self.draft.temp_id_document_tag, - first_name=self.cleaned_data['first_name'], - last_name=self.cleaned_data['last_name'], + first_name=self.cleaned_data['name'], email_address=self.cleaned_data['email'], author_order=0, - submission=self.draft) + submission=self.draft, + ) def save_new_draft_info(self): salt = hashlib.sha1(str(random.random())).hexdigest()[:5] @@ -400,18 +354,11 @@ class MetaDataForm(AutoPostForm): creation_date = forms.DateField(label=u'Creation date', required=True) pages = forms.IntegerField(label=u'Pages', required=True) abstract = forms.CharField(label=u'Abstract', widget=forms.Textarea, required=True) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - name = forms.CharField(required=True) - else: - first_name = forms.CharField(label=u'Given name', required=True) - last_name = forms.CharField(label=u'Last name', required=True) + name = forms.CharField(required=True) email = forms.EmailField(label=u'Email address', required=True) comments = forms.CharField(label=u'Comments to the secretariat', widget=forms.Textarea, required=False) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'name', 'email', 'comments'] - else: - fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'first_name', 'last_name', 'email', 'comments'] + fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'name', 'email', 'comments'] def __init__(self, *args, **kwargs): super(MetaDataForm, self).__init__(*args, **kwargs) @@ -422,43 +369,21 @@ class MetaDataForm(AutoPostForm): authors=[] if self.is_bound: for key, value in self.data.items(): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if key.startswith('name_'): - author = {'errors': {}} - index = key.replace('name_', '') - name = value.strip() - if not name: - author['errors']['name'] = 'This field is required' - email = self.data.get('email_%s' % index, '').strip() - if email and not email_re.search(email): - author['errors']['email'] = 'Enter a valid e-mail address' - if name or email: - author.update({'get_full_name': name, - 'email': (name, email), - 'index': index, - }) - authors.append(author) - - else: - if key.startswith('first_name_'): - author = {'errors': {}} - index = key.replace('first_name_', '') - first_name = value.strip() - if not first_name: - author['errors']['first_name'] = 'This field is required' - last_name = self.data.get('last_name_%s' % index, '').strip() - if not last_name: - author['errors']['last_name'] = 'This field is required' - email = self.data.get('email_%s' % index, '').strip() - if email and not email_re.search(email): - author['errors']['email'] = 'Enter a valid e-mail address' - if first_name or last_name or email: - author.update({'first_name': first_name, - 'last_name': last_name, - 'email': ('%s %s' % (first_name, last_name), email), - 'index': index, - }) - authors.append(author) + if key.startswith('name_'): + author = {'errors': {}} + index = key.replace('name_', '') + name = value.strip() + if not name: + author['errors']['name'] = 'This field is required' + email = self.data.get('email_%s' % index, '').strip() + if email and not email_re.search(email): + author['errors']['email'] = 'Enter a valid e-mail address' + if name or email: + author.update({'get_full_name': name, + 'email': (name, email), + 'index': index, + }) + authors.append(author) authors.sort(key=lambda x: x['index']) return authors @@ -491,7 +416,7 @@ class MetaDataForm(AutoPostForm): raise forms.ValidationError('Version field is not in NN format') if version_int > 99 or version_int < 0: raise forms.ValidationError('Version must be set between 00 and 99') - existing_revisions = [int(i.revision_display()) for i in InternetDraft.objects.filter(filename=self.draft.filename)] + existing_revisions = [int(i.rev) for i in Document.objects.filter(name=self.draft.filename)] expected = 0 if existing_revisions: expected = max(existing_revisions) + 1 @@ -536,14 +461,12 @@ class MetaDataForm(AutoPostForm): self.save_submitter_info() # submitter is author 0 for i, author in enumerate(self.authors): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # save full name - TempIdAuthors.objects.create( - id_document_tag=draft.temp_id_document_tag, - first_name=author["get_full_name"], - email_address=author["email"][1], - author_order=i + 1, - submission=draft) + TempIdAuthors.objects.create( + id_document_tag=draft.temp_id_document_tag, + first_name=author["get_full_name"], # save full name + email_address=author["email"][1], + author_order=i + 1, + submission=draft) def save(self, request): self.save_new_draft_info() @@ -556,7 +479,7 @@ class MetaDataForm(AutoPostForm): cc = [self.cleaned_data['email']] cc += [i['email'][1] for i in self.authors] if self.draft.group_acronym: - cc += [i.person.email()[1] for i in self.draft.group_acronym.wgchair_set.all()] + cc += [r.email.address for r in Role.objects.filter(group=self.draft.group_acronym, name="chair").select_related("email")] cc = list(set(cc)) submitter = self.draft.tempidauthors_set.get(author_order=0) send_mail(request, to_email, from_email, subject, 'submit/manual_post_mail.txt', { @@ -588,7 +511,7 @@ class PreapprovalForm(forms.Form): raise forms.ValidationError("Name ends with a dash.") acronym = components[2] if acronym not in self.groups.values_list('acronym', flat=True): - raise forms.ValidationError("WG acronym not recognized as one you can approve drafts for.") + raise forms.ValidationError("Group acronym not recognized as one you can approve drafts for.") if Preapproval.objects.filter(name=n): raise forms.ValidationError("Pre-approval for this name already exists.") diff --git a/ietf/submit/models.py b/ietf/submit/models.py index e73a91611..feb76fa17 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -1,11 +1,10 @@ -import re, datetime # +import re, datetime, hashlib from django.conf import settings from django.db import models -from django.utils.hashcompat import md5_constructor -from ietf.idtracker.models import InternetDraft, IETFWG from ietf.person.models import Person +from ietf.group.models import Group class IdSubmissionStatus(models.Model): @@ -22,7 +21,7 @@ class IdSubmissionDetail(models.Model): last_updated_date = models.DateField(null=True, blank=True) last_updated_time = models.CharField(null=True, blank=True, max_length=25) id_document_name = models.CharField(null=True, blank=True, max_length=255) - group_acronym = models.ForeignKey(IETFWG, null=True, blank=True) + group_acronym = models.ForeignKey(Group, null=True, blank=True) filename = models.CharField(null=True, blank=True, max_length=255, db_index=True) creation_date = models.DateField(null=True, blank=True) submission_date = models.DateField(null=True, blank=True) @@ -51,7 +50,7 @@ class IdSubmissionDetail(models.Model): return u"%s-%s" % (self.filename, self.revision) def create_hash(self): - self.submission_hash = md5_constructor(settings.SECRET_KEY + self.filename).hexdigest() + self.submission_hash = hashlib.md5(settings.SECRET_KEY + self.filename).hexdigest() def get_hash(self): if not self.submission_hash: @@ -68,14 +67,6 @@ class IdSubmissionDetail(models.Model): return '%s' % (self.submission_id, self.submission_hash, self.status) status_link.allow_tags = True - def confirmation_email_list(self): - try: - draft = InternetDraft.objects.get(filename=self.filename) - email_list = list(set(u'%s <%s>' % (i.person.ascii, i.email()) for i in draft.authors)) - except InternetDraft.DoesNotExist: - email_list = list(set(u'%s <%s>' % i.email() for i in self.tempidauthors_set.all())) - return email_list - def create_submission_hash(sender, instance, **kwargs): instance.create_hash() @@ -102,10 +93,6 @@ class TempIdAuthors(models.Model): middle_initial = models.CharField(blank=True, max_length=255, null=True) name_suffix = models.CharField(blank=True, max_length=255, null=True) - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'temp_id_authors' - def email(self): return (self.get_full_name(), self.email_address) diff --git a/ietf/submit/parsers/plain_parser.py b/ietf/submit/parsers/plain_parser.py index 40e92c054..cb9c52490 100644 --- a/ietf/submit/parsers/plain_parser.py +++ b/ietf/submit/parsers/plain_parser.py @@ -2,12 +2,9 @@ import datetime import re from django.conf import settings -from ietf.idtracker.models import InternetDraft, IETFWG from django.template.defaultfilters import filesizeformat from ietf.submit.parsers.base import FileParser -NONE_WG_PK = 1027 - class PlainParser(FileParser): diff --git a/ietf/submit/utils.py b/ietf/submit/utils.py index 9b6f9d684..3913c0091 100644 --- a/ietf/submit/utils.py +++ b/ietf/submit/utils.py @@ -7,14 +7,12 @@ from django.contrib.sites.models import Site from django.core.urlresolvers import reverse as urlreverse from django.template.loader import render_to_string -from ietf.idtracker.models import (InternetDraft, PersonOrOrgInfo, IETFWG, - IDAuthor, EmailAddress, IESGLogin, BallotInfo) -from ietf.submit.models import TempIdAuthors, IdSubmissionDetail, Preapproval from ietf.utils.mail import send_mail, send_mail_message from ietf.utils.log import log from ietf.utils import unaccent -from ietf.ietfauth.decorators import has_role +from ietf.ietfauth.utils import has_role +from ietf.submit.models import TempIdAuthors, IdSubmissionDetail, Preapproval from ietf.doc.models import * from ietf.person.models import Person, Alias, Email from ietf.doc.utils import add_state_change_event @@ -30,14 +28,11 @@ CANCELLED = -4 INITIAL_VERSION_APPROVAL_REQUESTED = 10 -# Not a real WG -NONE_WG = 1027 - def request_full_url(request, submission): subject = 'Full URL for managing submission of draft %s' % submission.filename from_email = settings.IDSUBMIT_FROM_EMAIL - to_email = submission.confirmation_email_list() + to_email = submission_confirmation_email_list(submission) url = settings.IDTRACKER_BASE_URL + urlreverse('draft_status_by_hash', kwargs=dict(submission_id=submission.submission_id, submission_hash=submission.get_hash())) @@ -49,7 +44,6 @@ def request_full_url(request, submission): def perform_post(request, submission): system = Person.objects.get(name="(System)") - group_id = submission.group_acronym_id or NONE_WG try: draft = Document.objects.get(name=submission.filename) save_document_in_history(draft) @@ -62,10 +56,11 @@ def perform_post(request, submission): draft.type_id = "draft" draft.time = datetime.datetime.now() draft.title = submission.id_document_name - if not (group_id == NONE_WG and draft.group and draft.group.type_id == "area"): + group = submission.group_acronym or Group.objects.get(type="individ") + if not (group.type_id == "individ" and draft.group and draft.group.type_id == "area"): # don't overwrite an assigned area if it's still an individual # submission - draft.group_id = group_id + draft.group_id = group.pk draft.rev = submission.revision draft.pages = submission.txt_page_count draft.abstract = submission.abstract @@ -142,12 +137,13 @@ def perform_post(request, submission): submission.save() -def send_announcements(submission, draft, state_change_msg): - announce_to_lists(request, submission) - if draft.idinternal and not draft.idinternal.rfc_flag: - announce_new_version(request, submission, draft, state_change_msg) - announce_to_authors(request, submission) - +def submission_confirmation_email_list(submission): + try: + doc = Document.objects.get(name=submission.filename) + email_list = [i.author.formatted_email() for i in doc.documentauthor_set.all()] + except Document.DoesNotExist: + email_list = [u'%s <%s>' % i.email() for i in submission.tempidauthors_set.all()] + return email_list def announce_to_lists(request, submission): authors = [] @@ -156,61 +152,28 @@ def announce_to_lists(request, submission): continue authors.append(i.get_full_name()) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - m = Message() - m.by = Person.objects.get(name="(System)") - if request.user.is_authenticated(): - try: - m.by = request.user.get_profile() - except Person.DoesNotExist: - pass - m.subject = 'I-D Action: %s-%s.txt' % (submission.filename, submission.revision) - m.frm = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL - m.to = settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL - if submission.group_acronym: - m.cc = submission.group_acronym.email_address - m.body = render_to_string('submit/announce_to_lists.txt', dict(submission=submission, - authors=authors, - settings=settings,)) - m.save() - m.related_docs.add(Document.objects.get(name=submission.filename)) + m = Message() + m.by = Person.objects.get(name="(System)") + if request.user.is_authenticated(): + try: + m.by = request.user.get_profile() + except Person.DoesNotExist: + pass + m.subject = 'I-D Action: %s-%s.txt' % (submission.filename, submission.revision) + m.frm = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL + m.to = settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL + if submission.group_acronym and submission.group_acronym.list_email: + m.cc = submission.group_acronym.list_email + m.body = render_to_string('submit/announce_to_lists.txt', dict(submission=submission, + authors=authors, + settings=settings,)) + m.save() + m.related_docs.add(Document.objects.get(name=submission.filename)) - send_mail_message(request, m) - else: - subject = 'I-D Action: %s-%s.txt' % (submission.filename, submission.revision) - from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL - to_email = [settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL] - if submission.group_acronym: - cc = [submission.group_acronym.email_address] - else: - cc = None - - send_mail(request, to_email, from_email, subject, 'submit/announce_to_lists.txt', - {'submission': submission, - 'authors': authors}, cc=cc, save_message=True) + send_mail_message(request, m) def announce_new_version(request, submission, draft, state_change_msg): - to_email = [] - if draft.idinternal.state_change_notice_to: - to_email.append(draft.idinternal.state_change_notice_to) - if draft.idinternal.job_owner: - to_email.append(draft.idinternal.job_owner.person.email()[1]) - try: - if draft.idinternal.ballot: - for p in draft.idinternal.ballot.positions.all(): - if p.discuss == 1 and p.ad.user_level == IESGLogin.AD_LEVEL: - to_email.append(p.ad.person.email()[1]) - except BallotInfo.DoesNotExist: - pass - subject = 'New Version Notification - %s-%s.txt' % (submission.filename, submission.revision) - from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL - send_mail(request, to_email, from_email, subject, 'submit/announce_new_version.txt', - {'submission': submission, - 'msg': state_change_msg}) - - -def announce_new_versionREDESIGN(request, submission, draft, state_change_msg): to_email = [] if draft.notify: to_email.append(draft.notify) @@ -241,16 +204,13 @@ def announce_new_versionREDESIGN(request, submission, draft, state_change_msg): {'submission': submission, 'msg': state_change_msg}) -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - announce_new_version = announce_new_versionREDESIGN - def announce_to_authors(request, submission): authors = submission.tempidauthors_set.all() - to_email = list(set(submission.confirmation_email_list() + [u'%s <%s>' % i.email() for i in authors])) + to_email = list(set(submission_confirmation_email_list(submission) + [u'%s <%s>' % i.email() for i in authors])) from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL subject = 'New Version Notification for %s-%s.txt' % (submission.filename, submission.revision) if submission.group_acronym: - wg = submission.group_acronym.group_acronym.acronym + wg = submission.group_acronym.acronym elif submission.filename.startswith('draft-iesg'): wg = 'IESG' else: @@ -261,64 +221,6 @@ def announce_to_authors(request, submission): 'wg': wg}) -def find_person(first_name, last_name, middle_initial, name_suffix, email): - person_list = None - if email: - person_list = PersonOrOrgInfo.objects.filter(emailaddress__address=email).distinct() - if person_list and len(person_list) == 1: - return person_list[0] - if not person_list: - person_list = PersonOrOrgInfo.objects.all() - person_list = person_list.filter(first_name=first_name, - last_name=last_name) - if middle_initial: - person_list = person_list.filter(middle_initial=middle_initial) - if name_suffix: - person_list = person_list.filter(name_suffix=name_suffix) - if person_list: - return person_list[0] - return None - - -def update_authors(draft, submission): - # TempAuthor of order 0 is submitter - new_authors = list(submission.tempidauthors_set.filter(author_order__gt=0)) - person_pks = [] - for author in new_authors: - person = find_person(author.first_name, author.last_name, - author.middle_initial, author.name_suffix, - author.email_address) - if not person: - person = PersonOrOrgInfo( - first_name=author.first_name, - last_name=author.last_name, - middle_initial=author.middle_initial or '', - name_suffix=author.name_suffix or '', - ) - person.save() - if author.email_address: - EmailAddress.objects.create( - address=author.email_address, - priority=1, - type='INET', - person_or_org=person, - ) - person_pks.append(person.pk) - try: - idauthor = IDAuthor.objects.get( - document=draft, - person=person, - ) - idauthor.author_order = author.author_order - except IDAuthor.DoesNotExist: - idauthor = IDAuthor( - document=draft, - person=person, - author_order=author.author_order, - ) - idauthor.save() - draft.authors.exclude(person__pk__in=person_pks).delete() - def get_person_from_author(author): persons = None @@ -376,7 +278,7 @@ def ensure_person_email_info_exists(author): return email -def update_authorsREDESIGN(draft, submission): +def update_authors(draft, submission): # order 0 is submitter authors = [] for author in submission.tempidauthors_set.exclude(author_order=0).order_by('author_order'): @@ -395,26 +297,6 @@ def update_authorsREDESIGN(draft, submission): draft.documentauthor_set.exclude(author__in=authors).delete() - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - update_authors = update_authorsREDESIGN - - -def get_person_for_user(user): - try: - return user.get_profile().person() - except: - return None - - -def is_secretariat(user): - if not user or not user.is_authenticated(): - return False - return bool(user.groups.filter(name='Secretariat')) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.accounts import is_secretariat, get_person_for_user - def move_docs(submission): for ext in submission.file_type.split(','): source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext)) @@ -485,7 +367,7 @@ class DraftValidation(object): return passes_idnits def get_working_group(self): - if self.draft.group_acronym and self.draft.group_acronym.pk == NONE_WG: + if self.draft.group_acronym and self.draft.group_acronym.type_id == "individ": return None return self.draft.group_acronym @@ -529,7 +411,7 @@ class DraftValidation(object): self.add_warning('title', 'Title is empty or was not found') def validate_wg(self): - if self.wg and not self.wg.status_id == IETFWG.ACTIVE: + if self.wg and self.wg.state_id != "active": self.add_warning('group', 'Group exists but is not an active group') def validate_abstract(self): @@ -543,7 +425,7 @@ class DraftValidation(object): if self.draft.status_id in [POSTED, POSTED_BY_SECRETARIAT]: return revision = self.draft.revision - existing_revisions = [int(i.revision_display()) for i in InternetDraft.objects.filter(filename=self.draft.filename)] + existing_revisions = [int(i.rev) for i in Document.objects.filter(name=self.draft.filename)] expected = 0 if existing_revisions: expected = max(existing_revisions) + 1 @@ -575,9 +457,9 @@ class DraftValidation(object): submitter = self.draft.tempidauthors_set.filter(author_order=0) if submitter: return submitter[0] - elif self.draft.submitter_tag: - try: - return PersonOrOrgInfo.objects.get(pk=self.draft.submitter_tag) - except PersonOrOrgInfo.DoesNotExist: - return False + # elif self.draft.submitter_tag: + # try: + # return PersonOrOrgInfo.objects.get(pk=self.draft.submitter_tag) + # except PersonOrOrgInfo.DoesNotExist: + # return False return None diff --git a/ietf/submit/views.py b/ietf/submit/views.py index a5e0a440d..023df6c55 100644 --- a/ietf/submit/views.py +++ b/ietf/submit/views.py @@ -9,13 +9,13 @@ from django.shortcuts import get_object_or_404 from django.shortcuts import render_to_response from django.template import RequestContext -from ietf.group.models import Group +from ietf.group.models import Group, Role from ietf.utils.mail import send_mail -from ietf.ietfauth.decorators import has_role, role_required +from ietf.ietfauth.utils import has_role, role_required from ietf.submit.models import IdSubmissionDetail, Preapproval from ietf.submit.forms import UploadForm, AutoPostForm, MetaDataForm, PreapprovalForm from ietf.submit.utils import UPLOADED, AWAITING_AUTHENTICATION, MANUAL_POST_REQUESTED, CANCELLED, POSTED, INITIAL_VERSION_APPROVAL_REQUESTED -from ietf.submit.utils import is_secretariat, get_approvable_submissions, get_preapprovals, get_recently_approved, get_person_for_user, perform_post, remove_docs, request_full_url +from ietf.submit.utils import get_approvable_submissions, get_preapprovals, get_recently_approved, perform_post, remove_docs, request_full_url from ietf.submit.utils import DraftValidation def submit_index(request): @@ -57,10 +57,9 @@ def submit_status(request): def _can_approve(user, detail): - person = get_person_for_user(user) if detail.status_id != INITIAL_VERSION_APPROVAL_REQUESTED or not detail.group_acronym: return None - if person in [i.person for i in detail.group_acronym.wgchair_set.all()] or is_secretariat(user): + if detail.group_acronym.has_role(user, "chair") or has_role(user, "Secretariat"): return True return False @@ -69,14 +68,14 @@ def _can_force_post(user, detail): if detail.status_id not in [MANUAL_POST_REQUESTED, AWAITING_AUTHENTICATION, INITIAL_VERSION_APPROVAL_REQUESTED]: return None - if is_secretariat(user): + if has_role(user, "Secretariat"): return True return False def _can_cancel(user, detail, submission_hash): if detail.status_id in [CANCELLED, POSTED]: return None - if is_secretariat(user): + if has_role(user, "Secretariat"): return True if submission_hash and detail.get_hash() == submission_hash: return True @@ -85,7 +84,7 @@ def _can_cancel(user, detail, submission_hash): def _can_edit(user, detail, submission_hash): if detail.status_id != UPLOADED: return None - if is_secretariat(user): + if has_role(user, "Secretariat"): return True if submission_hash and detail.get_hash() == submission_hash: return True @@ -128,7 +127,7 @@ def draft_status(request, submission_id, submission_hash=None, message=None): submitter = auto_post_form.save_submitter_info() subject = 'New draft waiting for approval: %s' % detail.filename from_email = settings.IDSUBMIT_FROM_EMAIL - to_email = list(set(i.person.email()[1] for i in detail.group_acronym.wgchair_set.all())) + to_email = [r.formatted_email() for r in Role.objects.filter(group=detail.group_acronym, name="chair").select_related("email", "person")] if to_email: authors = detail.tempidauthors_set.exclude(author_order=0).order_by('author_order') send_mail(request, to_email, from_email, subject, 'submit/submission_approval.txt', @@ -158,7 +157,7 @@ def draft_status(request, submission_id, submission_hash=None, message=None): show_notify_button = False if allow_edit == False or can_cancel == False: show_notify_button = True - if submission_hash is None and is_secretariat(request.user): + if submission_hash is None and has_role(request.user, "Secretariat"): submission_hash = detail.get_hash() # we'll need this when rendering the cancel button in the form return render_to_response('submit/draft_status.html', {'selected': 'status', @@ -285,7 +284,7 @@ def add_preapproval(request): groups = Group.objects.filter(type="wg").exclude(state="conclude").order_by("acronym").distinct() if not has_role(request.user, "Secretariat"): - groups = groups.filter(role__person=request.user.get_profile()) + groups = groups.filter(role__person__user=request.user) if request.method == "POST": form = PreapprovalForm(request.POST) @@ -310,7 +309,7 @@ def add_preapproval(request): def cancel_preapproval(request, preapproval_id): preapproval = get_object_or_404(Preapproval, pk=preapproval_id) - if not preapproval in get_preapprovals(request.user): + if preapproval not in get_preapprovals(request.user): raise HttpResponseForbidden("You do not have permission to cancel this preapproval.") if request.method == "POST" and request.POST.get("action", "") == "cancel": diff --git a/ietf/templates/submit/announce_to_lists.txt b/ietf/templates/submit/announce_to_lists.txt index 76146152d..0c195c5cd 100644 --- a/ietf/templates/submit/announce_to_lists.txt +++ b/ietf/templates/submit/announce_to_lists.txt @@ -1,6 +1,6 @@ {% autoescape off %} A New Internet-Draft is available from the on-line Internet-Drafts directories. -{% if submission.group_acronym %} This draft is a work item of the {{ submission.group_acronym.group_acronym.name }} Working Group of the IETF.{% endif %} +{% if submission.group_acronym %} This draft is a work item of the {{ submission.group_acronym.name }} Working Group of the IETF.{% endif %} Title : {{ submission.id_document_name }} Author(s) : {% for author in authors %}{{ author }}{% if not forloop.last %} From b46f0ba73331f8f79fd52439781e1fabfc1d35f7 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 6 Nov 2013 15:21:19 +0000 Subject: [PATCH 098/173] Get rid of submit/error_manager.py which is apparently unused - Legacy-Id: 6656 --- ietf/submit/error_manager.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 ietf/submit/error_manager.py diff --git a/ietf/submit/error_manager.py b/ietf/submit/error_manager.py deleted file mode 100644 index b4dcefd8f..000000000 --- a/ietf/submit/error_manager.py +++ /dev/null @@ -1,19 +0,0 @@ -from ietf.submit.models import IdSubmissionStatus - -class ErrorManager(object): - ERROR_CODES = { - 'DEFAULT': 'Unknow error', - 'INVALID_FILENAME': 111, - 'EXCEEDED_SIZE': 102, - } - - def get_error_str(self, key): - error_code = self.ERROR_CODES.get(key, self.ERROR_CODES['DEFAULT']) - if isinstance(error_code, basestring): - return '%s (%s)' % (key, error_code) - try: - return IdSubmissionStatus.objects.get(status_id=error_code).status_value - except IdSubmissionStatus.DoesNotExist: - return '%s (%s)' % (self.ERROR_CODES['DEFAULT'], key) - -MainErrorManager=ErrorManager() From 4635c1ce0fd09d41fdf90610dbdbadbd2c440e4d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 6 Nov 2013 15:23:08 +0000 Subject: [PATCH 099/173] Move helpers for the admin on IdSubmissionDetail to admin.py - Legacy-Id: 6657 --- ietf/submit/admin.py | 9 +++++++++ ietf/submit/models.py | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ietf/submit/admin.py b/ietf/submit/admin.py index 973a6e2c8..afdab4e9c 100644 --- a/ietf/submit/admin.py +++ b/ietf/submit/admin.py @@ -1,5 +1,7 @@ from django.core.urlresolvers import reverse as urlreverse from django.contrib import admin +from django.utils.safestring import mark_safe + from ietf.submit.models import * class IdSubmissionStatusAdmin(admin.ModelAdmin): @@ -19,6 +21,13 @@ class IdSubmissionDetailAdmin(admin.ModelAdmin): return '%s' % (url, instance.status) status_link.allow_tags = True + def draft_link(self, instance): + if instance.status_id in (-1, -2): + return '%s' % (instance.filename, instance.revision, instance.filename) + else: + return instance.filename + draft_link.allow_tags = True + admin.site.register(IdSubmissionDetail, IdSubmissionDetailAdmin) class PreapprovalAdmin(admin.ModelAdmin): diff --git a/ietf/submit/models.py b/ietf/submit/models.py index feb76fa17..1dfba99b2 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -57,15 +57,6 @@ class IdSubmissionDetail(models.Model): self.create_hash() self.save() return self.submission_hash - def draft_link(self): - if self.status_id == -1: - return '%s' % (self.filename, self.revision, self.filename) - else: - return self.filename - draft_link.allow_tags = True - def status_link(self): - return '%s' % (self.submission_id, self.submission_hash, self.status) - status_link.allow_tags = True def create_submission_hash(sender, instance, **kwargs): instance.create_hash() From 422a20fe3d75d70feb09adeaf69c24586ad95bb3 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 6 Nov 2013 15:24:22 +0000 Subject: [PATCH 100/173] Add replaces field which is apparently in the database, but not in models.py which confuses South - Legacy-Id: 6658 --- ietf/submit/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ietf/submit/models.py b/ietf/submit/models.py index 1dfba99b2..d5558b8f7 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -45,6 +45,7 @@ class IdSubmissionDetail(models.Model): invalid_version = models.IntegerField(null=True, blank=True) idnits_failed = models.IntegerField(null=True, blank=True) submission_hash = models.CharField(null=True, blank=True, max_length=255) + replaces = models.CharField(null=True, blank=True, max_length=255) def __unicode__(self): return u"%s-%s" % (self.filename, self.revision) From 8020650566e91e6db781175630dd9a1122fa5edd Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 11 Nov 2013 12:52:07 +0000 Subject: [PATCH 101/173] Fix color of links in form errors - Legacy-Id: 6700 --- static/css/base2.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/css/base2.css b/static/css/base2.css index 30533c8d4..938c7b71b 100644 --- a/static/css/base2.css +++ b/static/css/base2.css @@ -236,6 +236,8 @@ li.error { margin: 0.5em; background-color: #f44; } font-family: Arial, sans-serif; } +.errorlist a { color: #fff; } + .group-documents .search-results { margin-top: 1.5em; } table.milestones td.due { vertical-align: top; width: 80px; } From e6b0ef43233d67ede3ccc2f5927d752d88497752 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 11:04:12 +0000 Subject: [PATCH 102/173] Fix spelling error in validation - Legacy-Id: 6709 --- ietf/secr/drafts/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/secr/drafts/forms.py b/ietf/secr/drafts/forms.py index 5d4d13846..e5ced7dc3 100644 --- a/ietf/secr/drafts/forms.py +++ b/ietf/secr/drafts/forms.py @@ -375,7 +375,7 @@ class UploadForm(forms.Form): # ensure that the basename is unique base = splitext(txt.name)[0] if Document.objects.filter(name=base[:-3]): - raise forms.ValidationError, "This doucment filename already exists: %s" % base[:-3] + raise forms.ValidationError, "This document filename already exists: %s" % base[:-3] # ensure that rev is 00 if base[-2:] != '00': From 3a483461a246e25c4415b0ce1c2ea33cfffc72df Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 12:49:37 +0000 Subject: [PATCH 103/173] Add simple split filter, as the opposite of the built-in join filter - Legacy-Id: 6710 --- ietf/doc/templatetags/ietf_filters.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 5c19edb59..b7eb677f8 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -277,6 +277,10 @@ def truncate_ellipsis(text, arg): else: return escape(text) +@register.filter +def split(text, splitter=None): + return text.split(splitter) + @register.filter(name="wrap_long_lines") def wrap_long_lines(text): """Wraps long lines without loosing the formatting and indentation From e0be9bcb951c2c0e6777c6603c8a18d73cf4f840 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 12:50:24 +0000 Subject: [PATCH 104/173] Remove unused and bitrotten get_authors_email - Legacy-Id: 6711 --- ietf/secr/drafts/email.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ietf/secr/drafts/email.py b/ietf/secr/drafts/email.py index d72af76e9..534f0578f 100644 --- a/ietf/secr/drafts/email.py +++ b/ietf/secr/drafts/email.py @@ -78,19 +78,6 @@ def get_abbr_authors(draft): return result -def get_authors_email(draft): - """ - Takes a draft object and returns a string of authors suitable for an email to or cc field - """ - authors = [] - for a in draft.authors.all(): - initial = '' - if a.person.first_name: - initial = a.person.first_name[0] + '. ' - entry = '%s%s <%s>' % (initial,a.person.last_name,a.person.email()) - authors.append(entry) - return ', '.join(authors) - def get_last_revision(filename): """ This function takes a filename, in the same form it appears in the InternetDraft record, From 322e15e9a926c67e27809e4ed017a1d5af3226bd Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 12:52:33 +0000 Subject: [PATCH 105/173] Add generate_unique_key utility for generating access tokens (like abda31fee90aabe...) - Legacy-Id: 6712 --- ietf/utils/uniquekey.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ietf/utils/uniquekey.py diff --git a/ietf/utils/uniquekey.py b/ietf/utils/uniquekey.py new file mode 100644 index 000000000..9adcf0db6 --- /dev/null +++ b/ietf/utils/uniquekey.py @@ -0,0 +1,7 @@ +import time, random, hashlib + +from django.conf import settings + +def generate_unique_key(max_length=32): + return hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random())).hexdigest()[:max_length] + From fd01ddd215059d5eacaed7772db842fa316aa106 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 12:57:31 +0000 Subject: [PATCH 106/173] Make the mail code detect USING_DEBUG_EMAIL_SERVER which if set to true and EMAIL_HOST and EMAIL_PORT is set to localhost:1025 will turn on debugging which essentially makes it send emails; also added instructions for starting the debugging SMTP server bundled with Python python -m smtpd -n -c DebuggingServer localhost:1025 in a comment near the code - Legacy-Id: 6713 --- ietf/utils/mail.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ietf/utils/mail.py b/ietf/utils/mail.py index a99962b36..f53945825 100644 --- a/ietf/utils/mail.py +++ b/ietf/utils/mail.py @@ -176,8 +176,13 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F if extra: for k, v in extra.items(): if v: - msg[k] = v - if test_mode or settings.SERVER_MODE == 'production': + msg[k] = v + # start debug server with python -m smtpd -n -c DebuggingServer localhost:1025 + # then put USING_DEBUG_EMAIL_SERVER=True and EMAIL_HOST='localhost' + # and EMAIL_PORT=1025 in settings_local.py + debugging = getattr(settings, "USING_DEBUG_EMAIL_SERVER", False) and settings.EMAIL_HOST == 'localhost' and settings.EMAIL_PORT == 1025 + + if test_mode or debugging or settings.SERVER_MODE == 'production': send_smtp(msg, bcc) elif settings.SERVER_MODE == 'test': if toUser: @@ -188,7 +193,7 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F copy_to = settings.EMAIL_COPY_TO except AttributeError: copy_to = "ietf.tracker.archive+%s@gmail.com" % settings.SERVER_MODE - if copy_to and not test_mode: # if we're running automated tests, this copy is just annoying + if copy_to and not test_mode and not debugging: # if we're running automated tests, this copy is just annoying if bcc: msg['X-Tracker-Bcc']=bcc copy_email(msg, copy_to,originalBcc=bcc) From ecf68dbb0546667a0baf563a6fb931d244b88270 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 13:30:32 +0000 Subject: [PATCH 107/173] Revamp and clean up submit models: - Rename IdSubmissionDetail to Submission - Rename various submission fields to correspond to the conventions in the new schema - Use a name model for the states instead of IdSubmissionStatus - Drop the TempIdAuthor model which is based on splitting up author names - Add a simple textual SubmissionEvent for tracking events in the lifetime of a submission - Delete a bunch of obsolete fields - Make sure all submission have an access key so we can depend on it - Add state for when approval is needed from previous authors A couple of migrations take care of transforming the IdSubmissionDetail and moving data over/cleaning it up. Also revamp the submit view code: - Make form code do validation/cleaning only so there's a clear separation of concerns - Reduce uses of inheritance that made the code hard to follow - forms now don't inherit from each other, views don't call each other but instead reuse common utilities, templates share CSS/utilities instead of relying on inheritance - Move email rendering/sending to separate file - Drop the in-grown terminology use (auto post vs. manual posts) - Make the status page explain who is emailed for what purpose - Add history table with recorded events - Make the status page handle its post actions by itself instead of duplicating most of the setup logic in a number of simple views - Fix a couple of minor bugs and handle some edge cases better - Expand tests with a couple of more cases Possibly the submit tool could still use more help text added to explain the process, ideally what's explained in the tool instructions page should be inlined or self-evident. - Legacy-Id: 6714 --- ...bmission-confirmation-email-in-postfix-log | 8 +- ietf/name/fixtures/names.json | 95 ++++ ...0016_auto__add_draftsubmissionstatename.py | 214 +++++++ .../0017_populate_draftsubmissionstate.py | 204 +++++++ ietf/name/models.py | 5 + ietf/secr/drafts/views.py | 50 +- ietf/settings.py | 35 +- ietf/submit/admin.py | 34 +- ietf/submit/fixtures/idsubmissionstatus.xml | 102 ---- ietf/submit/forms.py | 499 +++++----------- ietf/submit/generate_fixtures.py | 34 -- ietf/submit/mail.py | 152 +++++ .../0003_turn_nulls_into_empty_strings.py | 297 ++++++++++ .../0004_fixup_idsubmissiondetail_fields.py | 327 +++++++++++ .../migrations/0005_fill_in_new_fields.py | 392 +++++++++++++ ...submissionstatus__del_field_submission_.py | 301 ++++++++++ ietf/submit/models.py | 129 ++--- ietf/submit/parsers/base.py | 24 +- ietf/submit/parsers/plain_parser.py | 40 +- ietf/submit/templatetags/submit_tags.py | 29 +- ietf/submit/test_submission.txt | 13 +- ietf/submit/tests.py | 389 ++++++++----- ietf/submit/urls.py | 35 +- ietf/submit/utils.py | 413 +++++--------- ietf/submit/views.py | 535 +++++++++++------- ietf/templates/base/left_menu.html | 2 +- .../templates/submit/announce_new_version.txt | 8 +- ietf/templates/submit/announce_to_authors.txt | 26 +- ietf/templates/submit/announce_to_lists.txt | 18 +- ietf/templates/submit/approval_request.txt | 33 ++ ietf/templates/submit/approvals.html | 67 +-- ietf/templates/submit/confirm_submission.html | 56 ++ ...rm_autopost.txt => confirm_submission.txt} | 5 +- ietf/templates/submit/draft_edit.html | 169 ------ ietf/templates/submit/draft_status.html | 257 --------- ietf/templates/submit/edit_submission.html | 114 ++++ .../{request_full_url.txt => full_url.txt} | 2 +- .../submit/last_confirmation_step.html | 26 - ietf/templates/submit/manual_post_mail.txt | 31 - ietf/templates/submit/manual_post_request.txt | 31 + .../submit/problem-reports-footer.html | 4 + ietf/templates/submit/search_submission.html | 18 + ietf/templates/submit/submission_approval.txt | 33 -- ietf/templates/submit/submission_status.html | 275 +++++++++ ietf/templates/submit/submit_base.html | 24 +- ietf/templates/submit/submit_index.html | 22 - ietf/templates/submit/submit_status.html | 27 - ietf/templates/submit/submitform.html | 53 -- ietf/templates/submit/submitter_form.html | 17 + ietf/templates/submit/tool_instructions.html | 4 +- ietf/templates/submit/upload_submission.html | 63 +++ static/css/submit.css | 31 + static/js/draft-submit.js | 64 ++- 53 files changed, 3809 insertions(+), 2027 deletions(-) create mode 100644 ietf/name/migrations/0016_auto__add_draftsubmissionstatename.py create mode 100644 ietf/name/migrations/0017_populate_draftsubmissionstate.py delete mode 100644 ietf/submit/fixtures/idsubmissionstatus.xml delete mode 100755 ietf/submit/generate_fixtures.py create mode 100644 ietf/submit/mail.py create mode 100644 ietf/submit/migrations/0003_turn_nulls_into_empty_strings.py create mode 100644 ietf/submit/migrations/0004_fixup_idsubmissiondetail_fields.py create mode 100644 ietf/submit/migrations/0005_fill_in_new_fields.py create mode 100644 ietf/submit/migrations/0006_auto__del_tempidauthors__del_idsubmissionstatus__del_field_submission_.py create mode 100644 ietf/templates/submit/approval_request.txt create mode 100644 ietf/templates/submit/confirm_submission.html rename ietf/templates/submit/{confirm_autopost.txt => confirm_submission.txt} (77%) delete mode 100644 ietf/templates/submit/draft_edit.html delete mode 100644 ietf/templates/submit/draft_status.html create mode 100644 ietf/templates/submit/edit_submission.html rename ietf/templates/submit/{request_full_url.txt => full_url.txt} (75%) delete mode 100644 ietf/templates/submit/last_confirmation_step.html delete mode 100644 ietf/templates/submit/manual_post_mail.txt create mode 100644 ietf/templates/submit/manual_post_request.txt create mode 100644 ietf/templates/submit/problem-reports-footer.html create mode 100644 ietf/templates/submit/search_submission.html delete mode 100644 ietf/templates/submit/submission_approval.txt create mode 100644 ietf/templates/submit/submission_status.html delete mode 100644 ietf/templates/submit/submit_index.html delete mode 100644 ietf/templates/submit/submit_status.html delete mode 100644 ietf/templates/submit/submitform.html create mode 100644 ietf/templates/submit/submitter_form.html create mode 100644 ietf/templates/submit/upload_submission.html create mode 100644 static/css/submit.css diff --git a/ietf/bin/find-submission-confirmation-email-in-postfix-log b/ietf/bin/find-submission-confirmation-email-in-postfix-log index 4b3ad2f7b..bf6004b19 100644 --- a/ietf/bin/find-submission-confirmation-email-in-postfix-log +++ b/ietf/bin/find-submission-confirmation-email-in-postfix-log @@ -31,7 +31,7 @@ from django.conf import settings from ietf.utils.path import path as Path -from ietf.submit.models import IdSubmissionDetail +from ietf.submit.models import Submission from ietf.doc.models import Document @@ -56,11 +56,11 @@ from_email = settings.IDSUBMIT_FROM_EMAIL if "<" in from_email: from_email = from_email.split("<")[1].split(">")[0] -submission = IdSubmissionDetail.objects.filter(filename=draft).order_by('-pk')[0] +submission = Submission.objects.filter(name=draft).order_by('-pk')[0] document = Document.objects.get(name=draft) emails = [ author.address for author in document.authors.all() ] -file = Path(settings.INTERNET_DRAFT_PATH) / ("%s-%s.txt"%(draft, submission.revision)) +file = Path(settings.INTERNET_DRAFT_PATH) / ("%s-%s.txt"%(draft, submission.rev)) upload_time = time.localtime(file.mtime) timestr1 = time.strftime("%b %d %H:%M", upload_time) @@ -87,4 +87,4 @@ for log in logfiles: for qi in queue_ids: if qi in line: sys.stdout.write(line) - \ No newline at end of file + diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json index adc0d3fbc..fe8b99f58 100644 --- a/ietf/name/fixtures/names.json +++ b/ietf/name/fixtures/names.json @@ -713,6 +713,101 @@ "desc": "" } }, + { + "pk": "uploaded", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 1, + "next_states": [ + "auth", + "aut-appr", + "grp-appr", + "manual", + "cancel" + ], + "used": true, + "name": "Uploaded", + "desc": "" + } + }, + { + "pk": "auth", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 2, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Submitter Authentication", + "desc": "" + } + }, + { + "pk": "aut-appr", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 3, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Approval from Previous Version Authors'", + "desc": "" + } + }, + { + "pk": "grp-appr", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 4, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Initial Version Approval", + "desc": "" + } + }, + { + "pk": "manual", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 5, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Manual Post", + "desc": "" + } + }, + { + "pk": "cancel", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 6, + "next_states": [], + "used": true, + "name": "Cancelled", + "desc": "" + } + }, + { + "pk": "posted", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 7, + "next_states": [], + "used": true, + "name": "Posted", + "desc": "" + } + }, { "pk": "comment", "model": "name.feedbacktype", diff --git a/ietf/name/migrations/0016_auto__add_draftsubmissionstatename.py b/ietf/name/migrations/0016_auto__add_draftsubmissionstatename.py new file mode 100644 index 000000000..c1891f69b --- /dev/null +++ b/ietf/name/migrations/0016_auto__add_draftsubmissionstatename.py @@ -0,0 +1,214 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'DraftSubmissionStateName' + db.create_table('name_draftsubmissionstatename', ( + ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('desc', self.gf('django.db.models.fields.TextField')(blank=True)), + ('used', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('order', self.gf('django.db.models.fields.IntegerField')(default=0)), + )) + db.send_create_signal('name', ['DraftSubmissionStateName']) + + # Adding M2M table for field next_states on 'DraftSubmissionStateName' + db.create_table('name_draftsubmissionstatename_next_states', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('from_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False)), + ('to_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False)) + )) + db.create_unique('name_draftsubmissionstatename_next_states', ['from_draftsubmissionstatename_id', 'to_draftsubmissionstatename_id']) + + + def backwards(self, orm): + + # Deleting model 'DraftSubmissionStateName' + db.delete_table('name_draftsubmissionstatename') + + # Removing M2M table for field next_states on 'DraftSubmissionStateName' + db.delete_table('name_draftsubmissionstatename_next_states') + + + models = { + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.dbtemplatetypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['name.DraftSubmissionStateName']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.feedbacktype': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupmilestonestatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.liaisonstatementpurposename': { + 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.nomineepositionstate': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.rolename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['name'] diff --git a/ietf/name/migrations/0017_populate_draftsubmissionstate.py b/ietf/name/migrations/0017_populate_draftsubmissionstate.py new file mode 100644 index 000000000..0d91bfa02 --- /dev/null +++ b/ietf/name/migrations/0017_populate_draftsubmissionstate.py @@ -0,0 +1,204 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + uploaded, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="uploaded", order=1, name="Uploaded") + submitter, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="auth", order=2, name="Awaiting Submitter Authentication") + author_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="aut-appr", order=3, name="Awaiting Approval from Previous Version Authors'") + group_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="grp-appr", order=4, name="Awaiting Initial Version Approval") + manual_post, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="manual", order=5, name="Awaiting Manual Post") + cancel, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="cancel", order=6, name="Cancelled") + posted, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="posted", order=7, name="Posted") + + uploaded.next_states = [submitter, author_approval, group_approval, manual_post, cancel] + submitter.next_states = [cancel, posted] + author_approval.next_states = [cancel, posted] + group_approval.next_states = [cancel, posted] + manual_post.next_states = [cancel, posted] + + + def backwards(self, orm): + "Write your backwards methods here." + + + models = { + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.dbtemplatetypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['name.DraftSubmissionStateName']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.feedbacktype': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupmilestonestatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.liaisonstatementpurposename': { + 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.nomineepositionstate': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.rolename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['name'] diff --git a/ietf/name/models.py b/ietf/name/models.py index 32b60819a..f339255f6 100644 --- a/ietf/name/models.py +++ b/ietf/name/models.py @@ -66,3 +66,8 @@ class FeedbackType(NameModel): """Type of feedback: questionnaires, nominations, comments""" class DBTemplateTypeName(NameModel): """reStructuredText, Plain, Django""" +class DraftSubmissionStateName(NameModel): + """Uploaded, Awaiting Submitter Authentication, Awaiting Approval from + Previous Version Authors, Awaiting Initial Version Approval, Awaiting + Manual Post, Cancelled, Posted""" + next_states = models.ManyToManyField('DraftSubmissionStateName', related_name="previous_states", blank=True) diff --git a/ietf/secr/drafts/views.py b/ietf/secr/drafts/views.py index b1c096d2d..a12a8ecda 100644 --- a/ietf/secr/drafts/views.py +++ b/ietf/secr/drafts/views.py @@ -17,7 +17,7 @@ from ietf.meeting.models import Meeting from ietf.name.models import StreamName from ietf.doc.models import Document, DocumentAuthor from ietf.doc.utils import augment_with_start_time -from ietf.submit.models import IdSubmissionDetail, Preapproval +from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName, SubmissionEvent from ietf.utils.draft import Draft from ietf.secr.proceedings.proc_utils import get_progress_stats from ietf.secr.sreq.views import get_meeting @@ -107,7 +107,7 @@ def process_files(request,draft): the basename, revision number and a list of file types. Basename and revision are assumed to be the same for all because this is part of the validation process. - It also creates the IdSubmissionDetail record WITHOUT saving, and places it in the + It also creates the Submission record WITHOUT saving, and places it in the session for saving in the final action step. ''' files = request.FILES @@ -124,29 +124,37 @@ def process_files(request,draft): wrapper = Draft(file.read(),file.name) handle_uploaded_file(file) - # create IdSubmissionDetail record, leaved unsaved - idsub = IdSubmissionDetail( - id_document_name=draft.title, - filename=basename, - revision=revision, - txt_page_count=draft.pages, - filesize=txt_size, - creation_date=wrapper.get_creation_date(), + # create Submission record, leaved unsaved + idsub = Submission( + name=basename, + title=draft.title, + rev=revision, + pages=draft.pages, + file_size=txt_size, + document_date=wrapper.get_creation_date(), submission_date=datetime.date.today(), idnits_message='idnits bypassed by manual posting', - temp_id_document_tag=None, - group_acronym_id=draft.group.id, + group_id=draft.group.id, remote_ip=request.META['REMOTE_ADDR'], first_two_pages=''.join(wrapper.pages[:2]), - status_id=-2, + state=DraftSubmissionStateName.objects.get(slug="posted"), abstract=draft.abstract, - file_type=','.join(file_type_list), - man_posted_date=datetime.date.today(), - man_posted_by=request.user.get_profile()) + file_types=','.join(file_type_list), + ) request.session['idsub'] = idsub return (filename,revision,file_type_list) +def post_submission(request): + submission = request.session['idsub'] + submission.save() + + SubmissionEvent.objects.create( + submission=submission, + by=request.user.person, + desc="Submitted and posted manually") + + def promote_files(draft, types): ''' This function takes one argument, a draft object. It then moves the draft files from @@ -308,8 +316,8 @@ def do_revision(draft, request): promote_files(new_draft, request.session['file_type']) # save the submission record - request.session['idsub'].save() - + post_submission(request) + # send announcement if we are in IESG process if new_draft.get_state('draft-iesg'): announcement_from_form(request.session['email'],by=request.user.get_profile()) @@ -356,8 +364,8 @@ def do_update(draft,request): promote_files(new_draft, request.session['file_type']) # save the submission record - request.session['idsub'].save() - + post_submission(request) + # send announcement announcement_from_form(request.session['email'],by=request.user.get_profile()) @@ -581,7 +589,7 @@ def add(request): promote_files(draft, file_type_list) # save the submission record - request.session['idsub'].save() + post_submission(request) request.session['action'] = 'add' diff --git a/ietf/settings.py b/ietf/settings.py index 3887097de..f420ac2dc 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -296,12 +296,6 @@ LIAISON_UNIVERSAL_FROM = 'Liaison Statement Management Tool %s' % (url, instance.status) + url = urlreverse('submit_submission_status_by_hash', + kwargs=dict(submission_id=instance.pk, + access_key=instance.access_key)) + return '%s' % (url, instance.state) status_link.allow_tags = True def draft_link(self, instance): - if instance.status_id in (-1, -2): - return '%s' % (instance.filename, instance.revision, instance.filename) + if instance.state_id == "posted": + return '%s' % (instance.name, instance.rev, instance.name) else: - return instance.filename + return instance.name draft_link.allow_tags = True -admin.site.register(IdSubmissionDetail, IdSubmissionDetailAdmin) +admin.site.register(Submission, SubmissionAdmin) class PreapprovalAdmin(admin.ModelAdmin): pass admin.site.register(Preapproval, PreapprovalAdmin) -class TempIdAuthorsAdmin(admin.ModelAdmin): - ordering = ["-id"] -admin.site.register(TempIdAuthors, TempIdAuthorsAdmin) - diff --git a/ietf/submit/fixtures/idsubmissionstatus.xml b/ietf/submit/fixtures/idsubmissionstatus.xml deleted file mode 100644 index 280d52693..000000000 --- a/ietf/submit/fixtures/idsubmissionstatus.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - Cancelled - - - Dead - - - Posted by the Secretariat - - - Posted - - - Ready To Post - - - Uploaded - - - ID NITS Passed - - - Initial Version Approval Required - - - Submitter Authentication Required - - - Manual Post Requested - - - External Meta-Data Required - - - Internal Database Has Been Updated - - - ID Announcement Scheduled - - - ID Tracker Notification Scheduled - - - Initial Version Approval Requested - - - Error - Plain text version does not exist - - - File size is larger than 20 MB - - - Duplicate Internet-Draft submission is currently in process. - - - Error - Simultaneous submission from the same IP address - - - Error - Auth key does not match - - - Error - No such Internet-Draft is currently in process - - - Error - Draft is not in an appropriate status for the requested page - - - Error - Unknown Request - - - Error - Invalid Email Address - - - Error - Direct Access is prohibited - - - Error - Invalid version number - - - Error - Invalid filename - - - Error - The document failed idnits verification - - - Creation Date must be within 3 days of the submission date. - - - Error – Not a valid submitter - - - Incorrect Meta-Data - - - The document does not contain a legitimate filename that start with draft-*. - - - Initial Version Approved - - \ No newline at end of file diff --git a/ietf/submit/forms.py b/ietf/submit/forms.py index d99b64294..5aaff363e 100644 --- a/ietf/submit/forms.py +++ b/ietf/submit/forms.py @@ -1,14 +1,9 @@ -import hashlib -import random import os -import subprocess import datetime from django import forms -from django.core.validators import email_re +from django.forms.formsets import formset_factory from django.conf import settings -from django.contrib.sites.models import Site -from django.template.loader import render_to_string from django.utils.html import mark_safe from django.core.urlresolvers import reverse as urlreverse @@ -17,44 +12,36 @@ import debug from ietf.group.models import Group, Role from ietf.doc.models import Document from ietf.meeting.models import Meeting -from ietf.submit.models import IdSubmissionDetail, TempIdAuthors, Preapproval -from ietf.submit.utils import MANUAL_POST_REQUESTED, UPLOADED, AWAITING_AUTHENTICATION, POSTED, POSTED_BY_SECRETARIAT, submission_confirmation_email_list +from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName +from ietf.submit.utils import validate_submission_rev, validate_submission_document_date from ietf.submit.parsers.pdf_parser import PDFParser from ietf.submit.parsers.plain_parser import PlainParser from ietf.submit.parsers.ps_parser import PSParser from ietf.submit.parsers.xml_parser import XMLParser from ietf.utils.mail import send_mail from ietf.utils.draft import Draft -from ietf.utils.pipe import pipe -from ietf.utils.log import log class UploadForm(forms.Form): - txt = forms.FileField(label=u'.txt format', required=True) xml = forms.FileField(label=u'.xml format', required=False) pdf = forms.FileField(label=u'.pdf format', required=False) ps = forms.FileField(label=u'.ps format', required=False) - fieldsets = [('Upload a draft', ('txt', 'xml', 'pdf', 'ps'))] - - class Media: - css = {'all': ("/css/liaisons.css", )} - - def __init__(self, *args, **kwargs): - self.request=kwargs.pop('request', None) - self.remote_ip=self.request.META.get('REMOTE_ADDR', None) + def __init__(self, request, *args, **kwargs): super(UploadForm, self).__init__(*args, **kwargs) - self.in_first_cut_off = False - self.idnits_message = None - self.shutdown = False - self.draft = None - self.filesize = None - self.group = None - self.file_type = [] - self.read_dates() - def read_dates(self): + self.remote_ip = request.META.get('REMOTE_ADDR', None) + + self.in_first_cut_off = False + self.cutoff_warning = "" + self.shutdown = False + self.set_cutoff_warnings() + + self.group = None + self.parsed_draft = None + + def set_cutoff_warnings(self): now = datetime.datetime.utcnow() first_cut_off = Meeting.get_first_cut_off() second_cut_off = Meeting.get_second_cut_off() @@ -74,166 +61,100 @@ class UploadForm(forms.Form): self.cutoff_warning = 'The cut-off time for the I-D submission was %02dh UTC, %s.
      The I-D submission tool will be reopened at %02dh local time at the IETF meeting location, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday) self.shutdown = True - def __unicode__(self): - return self.as_div() + def clean_file(self, field_name, parser_class): + f = self.cleaned_data[field_name] + if not f: + return f - def as_div(self): - return render_to_string('submit/submitform.html', {'form': self}) + parsed_info = parser_class(f).critical_parse() + if parsed_info.errors: + raise forms.ValidationError(parsed_info.errors) + + return f - def get_fieldsets(self): - if not self.fieldsets: - yield dict(name=None, fields=self) - else: - for fieldset, fields in self.fieldsets: - fieldset_dict = dict(name=fieldset, fields=[]) - for field_name in fields: - if field_name in self.fields.keyOrder: - fieldset_dict['fields'].append(self[field_name]) - if not fieldset_dict['fields']: - # if there is no fields in this fieldset, we continue to next fieldset - continue - yield fieldset_dict def clean_txt(self): - txt_file = self.cleaned_data['txt'] - if not txt_file: - return txt_file - parsed_info = PlainParser(txt_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - self.filesize=txt_file.size - return txt_file + return self.clean_file("txt", PlainParser) def clean_pdf(self): - pdf_file = self.cleaned_data['pdf'] - if not pdf_file: - return pdf_file - parsed_info = PDFParser(pdf_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - return pdf_file + return self.clean_file("pdf", PDFParser) def clean_ps(self): - ps_file = self.cleaned_data['ps'] - if not ps_file: - return ps_file - parsed_info = PSParser(ps_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - return ps_file + return self.clean_file("ps", PSParser) def clean_xml(self): - xml_file = self.cleaned_data['xml'] - if not xml_file: - return xml_file - parsed_info = XMLParser(xml_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - return xml_file + return self.clean_file("xml", XMLParser) def clean(self): if self.shutdown: raise forms.ValidationError('The tool is shut down') - self.check_paths() - if self.cleaned_data.get('txt', None): - self.get_draft() - self.group=self.get_working_group() - self.check_previous_submission() - if self.draft.revision == '00' and self.in_first_cut_off: + + # sanity check that paths exist (for development servers) + for s in ("IDSUBMIT_STAGING_PATH", "IDSUBMIT_IDNITS_BINARY", + "IDSUBMIT_REPOSITORY_PATH", "INTERNET_DRAFT_ARCHIVE_DIR"): + if not os.path.exists(getattr(settings, s)): + raise forms.ValidationError('%s defined in settings.py does not exist' % s) + + if self.cleaned_data.get('txt'): + # try to parse it + txt_file = self.cleaned_data['txt'] + txt_file.seek(0) + self.parsed_draft = Draft(txt_file.read(), txt_file.name) + txt_file.seek(0) + + if not self.parsed_draft.filename: + raise forms.ValidationError("Draft parser could not extract a valid draft name from the .txt file") + + # check group + self.group = self.deduce_group() + + # check existing + existing = Submission.objects.filter(name=self.parsed_draft.filename, rev=self.parsed_draft.revision).exclude(state__in=("posted", "cancel")) + if existing: + raise forms.ValidationError(mark_safe('Submission with same name and revision is currently being processed. Check the status here' % urlreverse("submit_submission_status", kwargs={ 'submission_id': existing[0].pk }))) + + # cut-off + if self.parsed_draft.revision == '00' and self.in_first_cut_off: raise forms.ValidationError(mark_safe(self.cutoff_warning)) - self.check_tresholds() + + # check thresholds + today = datetime.date.today() + + self.check_submissions_tresholds( + "for the draft %s" % self.parsed_draft.filename, + dict(name=self.parsed_draft.filename, rev=self.parsed_draft.revision, submission_date=today), + settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME, settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME_SIZE, + ) + self.check_submissions_tresholds( + "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_tresholds( + "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_tresholds( + "across all submitters", + dict(submission_date=today), + settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS, settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS_SIZE, + ) + return super(UploadForm, self).clean() - def check_tresholds(self): - filename = self.draft.filename - revision = self.draft.revision - remote_ip = self.remote_ip - today = datetime.date.today() + def check_submissions_tresholds(self, which, filter_kwargs, max_amount, max_size): + submissions = Submission.objects.filter(**filter_kwargs) - # Same draft by name - same_name = IdSubmissionDetail.objects.filter(filename=filename, revision=revision, submission_date=today) - if same_name.count() > settings.MAX_SAME_DRAFT_NAME: - raise forms.ValidationError('The same I-D cannot be submitted more than %s times a day' % settings.MAX_SAME_DRAFT_NAME) - if sum(i.filesize for i in same_name) > settings.MAX_SAME_DRAFT_NAME_SIZE * 1048576: - raise forms.ValidationError('The same I-D submission cannot exceed more than %s MByte a day' % settings.MAX_SAME_DRAFT_NAME_SIZE) + if len(submissions) > max_amount: + raise forms.ValidationError("Max submissions %s has been reached for today (maximum is %s submissions)." % (which, max_amount)) + if sum(s.file_size for s in submissions) > max_size * 1024 * 1024: + raise forms.ValidationError("Max uploaded amount %s has been reached for today (maximum is %s MB)." % (which, max_size)) - # Total from same ip - same_ip = IdSubmissionDetail.objects.filter(remote_ip=remote_ip, submission_date=today) - if same_ip.count() > settings.MAX_SAME_SUBMITTER: - raise forms.ValidationError('The same submitter cannot submit more than %s I-Ds a day' % settings.MAX_SAME_SUBMITTER) - if sum(i.filesize for i in same_ip) > settings.MAX_SAME_SUBMITTER_SIZE * 1048576: - raise forms.ValidationError('The same submitter cannot exceed more than %s MByte a day' % settings.MAX_SAME_SUBMITTER_SIZE) - - # Total in same group - if self.group: - same_group = IdSubmissionDetail.objects.filter(group_acronym=self.group, submission_date=today) - if same_group.count() > settings.MAX_SAME_WG_DRAFT: - raise forms.ValidationError('The same working group I-Ds cannot be submitted more than %s times a day' % settings.MAX_SAME_WG_DRAFT) - if sum(i.filesize for i in same_group) > settings.MAX_SAME_WG_DRAFT_SIZE * 1048576: - raise forms.ValidationError('Total size of same working group I-Ds cannot exceed %s MByte a day' % settings.MAX_SAME_WG_DRAFT_SIZE) - - - # Total drafts for today - total_today = IdSubmissionDetail.objects.filter(submission_date=today) - if total_today.count() > settings.MAX_DAILY_SUBMISSION: - raise forms.ValidationError('The total number of today\'s submission has reached the maximum number of submission per day') - if sum(i.filesize for i in total_today) > settings.MAX_DAILY_SUBMISSION_SIZE * 1048576: - raise forms.ValidationError('The total size of today\'s submission has reached the maximum size of submission per day') - - def check_paths(self): - self.staging_path = getattr(settings, 'IDSUBMIT_STAGING_PATH', None) - self.idnits = getattr(settings, 'IDSUBMIT_IDNITS_BINARY', None) - if not self.staging_path: - raise forms.ValidationError('IDSUBMIT_STAGING_PATH not defined in settings.py') - if not os.path.exists(self.staging_path): - raise forms.ValidationError('IDSUBMIT_STAGING_PATH defined in settings.py does not exist') - if not self.idnits: - raise forms.ValidationError('IDSUBMIT_IDNITS_BINARY not defined in settings.py') - if not os.path.exists(self.idnits): - raise forms.ValidationError('IDSUBMIT_IDNITS_BINARY defined in settings.py does not exist') - - def check_previous_submission(self): - filename = self.draft.filename - revision = self.draft.revision - existing = IdSubmissionDetail.objects.filter(filename=filename, revision=revision, - status__pk__gte=0, status__pk__lt=100) - if existing: - raise forms.ValidationError(mark_safe('Duplicate Internet-Draft submission is currently in process. Check it here' % existing[0].pk)) - - def get_draft(self): - if self.draft: - return self.draft - txt_file = self.cleaned_data['txt'] - txt_file.seek(0) - self.draft = Draft(txt_file.read(), txt_file.name) - txt_file.seek(0) - return self.draft - - def save(self): - for ext in ['txt', 'pdf', 'xml', 'ps']: - fd = self.cleaned_data[ext] - if not fd: - continue - self.file_type.append('.%s' % ext) - filename = os.path.join(self.staging_path, '%s-%s.%s' % (self.draft.filename, self.draft.revision, ext)) - destination = open(filename, 'wb+') - for chunk in fd.chunks(): - destination.write(chunk) - destination.close() - self.check_idnits() - return self.save_draft_info(self.draft) - - def check_idnits(self): - filepath = os.path.join(self.staging_path, '%s-%s.txt' % (self.draft.filename, self.draft.revision)) - #p = subprocess.Popen([self.idnits, '--submitcheck', '--nitcount', filepath], stdout=subprocess.PIPE) - cmd = "%s --submitcheck --nitcount %s" % (self.idnits, filepath) - code, out, err = pipe(cmd) - if code != 0: - log("idnits error: %s:\n Error %s: %s" %(cmd, code, err)) - self.idnits_message = out - - def get_working_group(self): - name = self.draft.filename + def deduce_group(self): + """Figure out group from name or previously submitted draft, returns None if individual.""" + name = self.parsed_draft.filename existing_draft = Document.objects.filter(name=name, type="draft") if existing_draft: group = existing_draft[0].group @@ -245,11 +166,11 @@ class UploadForm(forms.Form): if name.startswith('draft-ietf-') or name.startswith("draft-irtf-"): components = name.split("-") if len(components) < 3: - raise forms.ValidationError("The draft name \"%s\" is missing a third part, please rename it") + raise forms.ValidationError(u"The draft name \"%s\" is missing a third part, please rename it" % name) if components[1] == "ietf": group_type = "wg" - else: + elif components[1] == "irtf": group_type = "rg" # first check groups with dashes @@ -261,109 +182,51 @@ class UploadForm(forms.Form): return Group.objects.get(acronym=components[2], type=group_type) except Group.DoesNotExist: raise forms.ValidationError('There is no active group with acronym \'%s\', please rename your draft' % components[2]) + elif name.startswith("draft-iab-"): return Group.objects.get(acronym="iab") + else: return None - def save_draft_info(self, draft): - detail = IdSubmissionDetail.objects.create( - id_document_name=draft.get_title(), - filename=draft.filename, - revision=draft.revision, - txt_page_count=draft.get_pagecount(), - filesize=self.filesize, - creation_date=draft.get_creation_date(), - submission_date=datetime.date.today(), - idnits_message=self.idnits_message, - temp_id_document_tag=-1, - group_acronym=self.group, - remote_ip=self.remote_ip, - first_two_pages=''.join(draft.pages[:2]), - status_id=UPLOADED, - abstract=draft.get_abstract(), - file_type=','.join(self.file_type), - ) - for order, author in enumerate(draft.get_author_list(), start=1): - full_name, first_name, middle_initial, last_name, name_suffix, email, company = author - # save full name - TempIdAuthors.objects.create( - id_document_tag=-1, - first_name=full_name.strip(), - email_address=(email or "").strip(), - author_order=order, - submission=detail) - return detail - - -class AutoPostForm(forms.Form): - +class NameEmailForm(forms.Form): + """For validating supplied submitter and author information.""" name = forms.CharField(required=True) - email = forms.EmailField(label=u'Email address', required=True) + email = forms.EmailField(label=u'Email address') def __init__(self, *args, **kwargs): - self.draft = kwargs.pop('draft', None) - self.validation = kwargs.pop('validation', None) - self.replaces = kwargs.pop('replaces', None) - super(AutoPostForm, self).__init__(*args, **kwargs) + email_required = kwargs.pop("email_required", True) + super(NameEmailForm, self).__init__(*args, **kwargs) - def get_author_buttons(self): - buttons = [] - for i in self.validation.authors: - buttons.append('' - % dict(name=i.get_full_name(), - email=i.email()[1] or '')) - return "".join(buttons) + self.fields["email"].required = email_required + self.fields["name"].widget.attrs["class"] = "name" + self.fields["email"].widget.attrs["class"] = "email" - def save(self, request): - self.save_submitter_info() - self.save_new_draft_info() - self.send_confirmation_mail(request) + def clean_name(self): + return self.cleaned_data["name"].replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip() - def send_confirmation_mail(self, request): - subject = 'Confirmation for Auto-Post of I-D %s' % self.draft.filename - from_email = settings.IDSUBMIT_FROM_EMAIL - to_email = submission_confirmation_email_list(self.draft) + def clean_email(self): + return self.cleaned_data["email"].replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip() - confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_confirm', kwargs=dict(submission_id=self.draft.submission_id, auth_key=self.draft.auth_key)) - status_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_status_by_hash', kwargs=dict(submission_id=self.draft.submission_id, submission_hash=self.draft.get_hash())) - - send_mail(request, to_email, from_email, subject, 'submit/confirm_autopost.txt', - { 'draft': self.draft, 'confirm_url': confirm_url, 'status_url': status_url }) + def cleaned_line(self): + line = self.cleaned_data["name"] + email = self.cleaned_data.get("email") + if email: + line += u" <%s>" % email + return line - def save_submitter_info(self): - return TempIdAuthors.objects.create( - id_document_tag=self.draft.temp_id_document_tag, - first_name=self.cleaned_data['name'], - email_address=self.cleaned_data['email'], - author_order=0, - submission=self.draft, - ) +class EditSubmissionForm(forms.ModelForm): + title = forms.CharField(required=True, max_length=255) + rev = forms.CharField(label=u'Revision', max_length=2, required=True) + document_date = forms.DateField(required=True) + pages = forms.IntegerField(required=True) + abstract = forms.CharField(widget=forms.Textarea, required=True) - def save_new_draft_info(self): - salt = hashlib.sha1(str(random.random())).hexdigest()[:5] - self.draft.auth_key = hashlib.sha1(salt+self.cleaned_data['email']).hexdigest() - self.draft.status_id = AWAITING_AUTHENTICATION - self.draft.save() + note = forms.CharField(label=mark_safe(u'Comment to
      the Secretariat'), widget=forms.Textarea, required=False) - -class MetaDataForm(AutoPostForm): - - title = forms.CharField(label=u'Title', required=True) - version = forms.CharField(label=u'Version', required=True) - creation_date = forms.DateField(label=u'Creation date', required=True) - pages = forms.IntegerField(label=u'Pages', required=True) - abstract = forms.CharField(label=u'Abstract', widget=forms.Textarea, required=True) - name = forms.CharField(required=True) - email = forms.EmailField(label=u'Email address', required=True) - comments = forms.CharField(label=u'Comments to the secretariat', widget=forms.Textarea, required=False) - - fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'name', 'email', 'comments'] - - def __init__(self, *args, **kwargs): - super(MetaDataForm, self).__init__(*args, **kwargs) - self.set_initials() - self.authors = self.get_initial_authors() + class Meta: + model = Submission + fields = ['title', 'rev', 'document_date', 'pages', 'abstract', 'note'] def get_initial_authors(self): authors=[] @@ -379,117 +242,33 @@ class MetaDataForm(AutoPostForm): if email and not email_re.search(email): author['errors']['email'] = 'Enter a valid e-mail address' if name or email: - author.update({'get_full_name': name, - 'email': (name, email), + author.update({'name': name, + 'email': email, 'index': index, }) authors.append(author) authors.sort(key=lambda x: x['index']) return authors - def set_initials(self): - self.fields['pages'].initial=self.draft.txt_page_count - self.fields['creation_date'].initial=self.draft.creation_date - self.fields['version'].initial=self.draft.revision - self.fields['abstract'].initial=self.draft.abstract - self.fields['title'].initial=self.draft.id_document_name + def clean_rev(self): + rev = self.cleaned_data["rev"] - def clean_creation_date(self): - creation_date = self.cleaned_data.get('creation_date', None) - if not creation_date: - return None - submit_date = self.draft.submission_date - if (creation_date + datetime.timedelta(days=3) < submit_date or - creation_date - datetime.timedelta(days=3) > submit_date): - raise forms.ValidationError('Creation Date must be within 3 days of submission date') - return creation_date + if len(rev) == 1: + rev = "0" + rev - def clean_version(self): - version = self.cleaned_data.get('version', None) - if not version: - return None - if len(version) > 2: - raise forms.ValidationError('Version field is not in NN format') - try: - version_int = int(version) - except ValueError: - raise forms.ValidationError('Version field is not in NN format') - if version_int > 99 or version_int < 0: - raise forms.ValidationError('Version must be set between 00 and 99') - existing_revisions = [int(i.rev) for i in Document.objects.filter(name=self.draft.filename)] - expected = 0 - if existing_revisions: - expected = max(existing_revisions) + 1 - if version_int != expected: - raise forms.ValidationError('Invalid Version Number (Version %02d is expected)' % expected) - return version + error = validate_submission_rev(self.instance.name, rev) + if error: + raise forms.ValidationError(error) - def clean(self): - if bool([i for i in self.authors if i['errors']]): - raise forms.ValidationError('Please fix errors in author list') - return super(MetaDataForm, self).clean() + return rev - def get_authors(self): - if not self.is_bound: - return self.validation.get_authors() - else: - return self.authors - - def move_docs(self, draft, revision): - old_revision = draft.revision - for ext in draft.file_type.split(','): - source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (draft.filename, old_revision, ext)) - dest = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (draft.filename, revision, ext)) - os.rename(source, dest) - - def save_new_draft_info(self): - draft = self.draft - draft.id_document_name = self.cleaned_data['title'] - if draft.revision != self.cleaned_data['version']: - self.move_docs(draft, self.cleaned_data['version']) - draft.revision = self.cleaned_data['version'] - draft.creation_date = self.cleaned_data['creation_date'] - draft.txt_page_count = self.cleaned_data['pages'] - draft.abstract = self.cleaned_data['abstract'] - draft.comment_to_sec = self.cleaned_data['comments'] - draft.status_id = MANUAL_POST_REQUESTED - draft.save() - - # sync authors - draft.tempidauthors_set.all().delete() - - self.save_submitter_info() # submitter is author 0 - - for i, author in enumerate(self.authors): - TempIdAuthors.objects.create( - id_document_tag=draft.temp_id_document_tag, - first_name=author["get_full_name"], # save full name - email_address=author["email"][1], - author_order=i + 1, - submission=draft) - - def save(self, request): - self.save_new_draft_info() - self.send_mail_to_secretariat(request) - - def send_mail_to_secretariat(self, request): - subject = 'Manual Post Requested for %s' % self.draft.filename - from_email = settings.IDSUBMIT_FROM_EMAIL - to_email = settings.IDSUBMIT_TO_EMAIL - cc = [self.cleaned_data['email']] - cc += [i['email'][1] for i in self.authors] - if self.draft.group_acronym: - cc += [r.email.address for r in Role.objects.filter(group=self.draft.group_acronym, name="chair").select_related("email")] - cc = list(set(cc)) - submitter = self.draft.tempidauthors_set.get(author_order=0) - send_mail(request, to_email, from_email, subject, 'submit/manual_post_mail.txt', { - 'form': self, - 'draft': self.draft, - 'url': settings.IDTRACKER_BASE_URL + urlreverse('draft_status', kwargs=dict(submission_id=self.draft.submission_id)), - 'submitter': submitter - }, - cc=cc) + def clean_document_date(self): + document_date = self.cleaned_data['document_date'] + error = validate_submission_document_date(self.instance.submission_date, document_date) + if error: + raise forms.ValidationError(error) + return document_date class PreapprovalForm(forms.Form): name = forms.CharField(max_length=255, required=True, label="Pre-approved name", initial="draft-ietf-") @@ -515,7 +294,7 @@ class PreapprovalForm(forms.Form): if Preapproval.objects.filter(name=n): raise forms.ValidationError("Pre-approval for this name already exists.") - if IdSubmissionDetail.objects.filter(status__in=[POSTED, POSTED_BY_SECRETARIAT ], filename=n): + if Submission.objects.filter(state="posted", name=n): raise forms.ValidationError("A draft with this name has already been submitted and accepted. A pre-approval would not make any difference.") return n diff --git a/ietf/submit/generate_fixtures.py b/ietf/submit/generate_fixtures.py deleted file mode 100755 index cbda2392d..000000000 --- a/ietf/submit/generate_fixtures.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/python - -# boiler plate -import os, sys - -ietf_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../ietf')) - -sys.path.insert(0, ietf_path) - -from django.core.management import setup_environ -import settings -setup_environ(settings) - -# script -from django.core.serializers import serialize -from django.db.models import Q - -def output(name, qs): - try: - f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.xml" % name), 'w') - f.write(serialize("xml", qs, indent=4)) - f.close() - except: - from django.db import connection - from pprint import pprint - pprint(connection.queries) - raise - -# pick all name models directly out of the module -names = [] - -from ietf.submit.models import IdSubmissionStatus - -output("idsubmissionstatus", IdSubmissionStatus.objects.all()) diff --git a/ietf/submit/mail.py b/ietf/submit/mail.py new file mode 100644 index 000000000..bfe0f8584 --- /dev/null +++ b/ietf/submit/mail.py @@ -0,0 +1,152 @@ +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse +from django.contrib.sites.models import Site +from django.template.loader import render_to_string + +from ietf.utils.mail import send_mail, send_mail_message +from ietf.doc.models import Document +from ietf.person.models import Person +from ietf.group.models import Role +from ietf.message.models import Message + +def submission_confirmation_email_list(submission): + try: + doc = Document.objects.get(name=submission.name) + email_list = [i.author.formatted_email() for i in doc.documentauthor_set.all()] + except Document.DoesNotExist: + email_list = [u"%s <%s>" % (author["name"], author["email"]) + for author in submission.authors_parsed() if author["email"]] + if submission.submitter_parsed()["email"] and submission.submitter not in email_list: + email_list.append(submission.submitter) + return email_list + +def send_submission_confirmation(request, submission): + subject = 'Confirm submission of I-D %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = submission_confirmation_email_list(submission) + + confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_confirm_submission', kwargs=dict(submission_id=submission.pk, auth_key=submission.auth_key)) + status_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_key=submission.access_key)) + + send_mail(request, to_email, from_email, subject, 'submit/confirm_submission.txt', { + 'submission': submission, + 'confirm_url': confirm_url, + 'status_url': status_url, + }) + + return to_email + +def send_full_url(request, submission): + subject = 'Full URL for managing submission of draft %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = submission_confirmation_email_list(submission) + url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', + kwargs=dict(submission_id=submission.pk, + access_key=submission.access_key)) + send_mail(request, to_email, from_email, subject, 'submit/full_url.txt', { + 'submission': submission, + 'url': url, + }) + + return to_email + +def send_approval_request_to_group(request, submission): + subject = 'New draft waiting for approval: %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = [r.formatted_email() for r in Role.objects.filter(group=submission.group, name="chair").select_related("email", "person")] + if not to_email: + return to_email + + send_mail(request, to_email, from_email, subject, 'submit/approval_request.txt', { + 'submission': submission, + 'domain': Site.objects.get_current().domain, + }) + + return to_email + +def send_manual_post_request(request, submission, errors): + subject = u'Manual Post Requested for %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = settings.IDSUBMIT_TO_EMAIL + + cc = [submission.submitter] + cc += [u"%s <%s>" % (author["name"], author["email"]) + for author in submission.authors_parsed() if author["email"]] + if submission.group: + cc += [r.formatted_email() for r in Role.objects.filter(group=submission.group, name="chair").select_related("email", "person")] + cc = list(set(cc)) + + send_mail(request, to_email, from_email, subject, 'submit/manual_post_request.txt', { + 'submission': submission, + 'url': settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status', kwargs=dict(submission_id=submission.pk)), + 'errors': errors, + }, cc=cc) + + +def announce_to_lists(request, submission): + m = Message() + m.by = Person.objects.get(name="(System)") + if request.user.is_authenticated(): + try: + m.by = request.user.get_profile() + except Person.DoesNotExist: + pass + m.subject = 'I-D Action: %s-%s.txt' % (submission.name, submission.rev) + m.frm = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL + m.to = settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL + if submission.group and submission.group.list_email: + m.cc = submission.group.list_email + m.body = render_to_string('submit/announce_to_lists.txt', + dict(submission=submission, + settings=settings)) + m.save() + m.related_docs.add(Document.objects.get(name=submission.name)) + + send_mail_message(request, m) + + +def announce_new_version(request, submission, draft, state_change_msg): + to_email = [] + if draft.notify: + to_email.append(draft.notify) + if draft.ad: + to_email.append(draft.ad.role_email("ad").address) + + if draft.stream_id == "iab": + to_email.append("IAB Stream ") + elif draft.stream_id == "ise": + to_email.append("Independent Submission Editor ") + elif draft.stream_id == "irtf": + to_email.append("IRSG ") + + # if it has been sent to the RFC Editor, keep them in the loop + if draft.get_state_slug("draft-iesg") in ("ann", "rfcqueue"): + to_email.append("RFC Editor ") + + active_ballot = draft.active_ballot() + if active_ballot: + for ad, pos in active_ballot.active_ad_positions().iteritems(): + if pos and pos.pos_id == "discuss": + to_email.append(ad.role_email("ad").address) + + if to_email: + subject = 'New Version Notification - %s-%s.txt' % (submission.name, submission.rev) + from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL + send_mail(request, to_email, from_email, subject, 'submit/announce_new_version.txt', + {'submission': submission, + 'msg': state_change_msg}) + +def announce_to_authors(request, submission): + authors = [u"%s <%s>" % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]] + to_email = list(set(submission_confirmation_email_list(submission) + authors)) + from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL + subject = 'New Version Notification for %s-%s.txt' % (submission.name, submission.rev) + if submission.group: + group = submission.group.acronym + elif submission.name.startswith('draft-iesg'): + group = 'IESG' + else: + group = 'Individual Submission' + send_mail(request, to_email, from_email, subject, 'submit/announce_to_authors.txt', + {'submission': submission, + 'group': group}) diff --git a/ietf/submit/migrations/0003_turn_nulls_into_empty_strings.py b/ietf/submit/migrations/0003_turn_nulls_into_empty_strings.py new file mode 100644 index 000000000..4030ab46b --- /dev/null +++ b/ietf/submit/migrations/0003_turn_nulls_into_empty_strings.py @@ -0,0 +1,297 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + nullable_string_fields = [ + "id_document_name", + "comment_to_sec", + "filename", + "revision", + "replaces", + "file_type", + "abstract", + "idnits_message", + "first_two_pages", + "remote_ip", + "auth_key", + "submission_hash", + ] + + for f in nullable_string_fields: + orm.IdSubmissionDetail.objects.filter(**{ f: None}).update(**{ f: ""}) + + def backwards(self, orm): + pass + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.docalias': { + 'Meta': {'object_name': 'DocAlias'}, + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'related': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'reversely_related_document_set'", 'blank': 'True', 'through': "orm['doc.RelatedDocument']", 'to': "orm['doc.DocAlias']"}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.relateddocument': { + 'Meta': {'object_name': 'RelatedDocument'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'group.ietfwg': { + 'Meta': {'object_name': 'IETFWG', 'db_table': "'group_group'", '_ormbases': ['group.Group'], 'proxy': 'True'} + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + 'submit.idsubmissiondetail': { + 'Meta': {'object_name': 'IdSubmissionDetail'}, + 'abstract': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'comment_to_sec': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'filesize': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'group_acronym': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'id_document_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'idnits_failed': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'invalid_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'last_updated_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_updated_time': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True', 'blank': 'True'}), + 'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'revision': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}), + 'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'submission_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'submission_hash': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'temp_id_document_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'txt_page_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'warning_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'wg_submission': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'submit.idsubmissionstatus': { + 'Meta': {'object_name': 'IdSubmissionStatus'}, + 'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.tempidauthors': { + 'Meta': {'object_name': 'TempIdAuthors'}, + 'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'id_document_tag': ('django.db.models.fields.IntegerField', [], {}), + 'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionDetail']"}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/migrations/0004_fixup_idsubmissiondetail_fields.py b/ietf/submit/migrations/0004_fixup_idsubmissiondetail_fields.py new file mode 100644 index 000000000..235f9ac42 --- /dev/null +++ b/ietf/submit/migrations/0004_fixup_idsubmissiondetail_fields.py @@ -0,0 +1,327 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + depends_on = ( + ("name", "0017_populate_draftsubmissionstate"), + ) + + def forwards(self, orm): + db.rename_table('submit_idsubmissiondetail', 'submit_submission') + + db.rename_column('submit_submission', 'submission_id', "id") + + db.rename_column('submit_submission', 'id_document_name', "title") + db.alter_column('submit_submission', 'title', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True)) + db.rename_column('submit_submission', 'creation_date', "document_date") + db.rename_column('submit_submission', 'group_acronym_id', 'group_id') + db.rename_column('submit_submission', 'txt_page_count', 'pages') + db.rename_column('submit_submission', 'comment_to_sec', 'note') + db.alter_column('submit_submission', 'note', self.gf('django.db.models.fields.TextField')(default='', blank=True)) + db.rename_column('submit_submission', 'filename', 'name') + db.alter_column('submit_submission', 'name', self.gf('django.db.models.fields.CharField')(db_index=True, default='', max_length=255, blank=True)) + db.rename_column('submit_submission', 'filesize', 'file_size') + db.rename_column('submit_submission', 'revision', 'rev') + db.alter_column('submit_submission', 'rev', self.gf('django.db.models.fields.CharField')(default='', max_length=3, blank=True)) + + db.delete_column('submit_submission', 'idnits_failed') + db.delete_column('submit_submission', 'invalid_version') + db.delete_column('submit_submission', 'temp_id_document_tag') + db.delete_column('submit_submission', 'error_message') + db.delete_column('submit_submission', 'wg_submission') + db.delete_column('submit_submission', 'warning_message') + db.delete_column('submit_submission', 'last_updated_date') + db.delete_column('submit_submission', 'last_updated_time') + + db.add_column('submit_submission', 'state', self.gf('django.db.models.fields.related.ForeignKey')(default='uploaded', to=orm['name.DraftSubmissionStateName'])) + db.add_column('submit_submission', 'authors', self.gf('django.db.models.fields.TextField')(default="")) + db.add_column('submit_submission', 'submitter', self.gf('django.db.models.fields.CharField')(max_length=255, default="")) + + db.alter_column('submit_submission', 'submission_date', self.gf('django.db.models.fields.DateField')()) + + db.alter_column('submit_submission', 'replaces', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) + + db.rename_column('submit_submission', 'file_type', 'file_types') + db.alter_column('submit_submission', 'file_types', self.gf('django.db.models.fields.CharField')(default='', max_length=50)) + + db.alter_column('submit_submission', 'abstract', self.gf('django.db.models.fields.TextField')(default='')) + + db.alter_column('submit_submission', 'idnits_message', self.gf('django.db.models.fields.TextField')(default='')) + + db.alter_column('submit_submission', 'first_two_pages', self.gf('django.db.models.fields.TextField')(default='')) + + db.alter_column('submit_submission', 'remote_ip', self.gf('django.db.models.fields.CharField')(default='', max_length=100)) + + db.alter_column('submit_submission', 'auth_key', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) + + db.rename_column('submit_submission', 'submission_hash', "access_key") + db.alter_column('submit_submission', 'access_key', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) + + db.create_table('submit_submissionevent', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('submission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.Submission'])), + ('time', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Person'], null=True, blank=True)), + ('desc', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal('submit', ['SubmissionEvent']) + + def backwards(self, orm): + pass + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + 'submit.submission': { + 'Meta': {'object_name': 'Submission'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'default': "'uploaded'", 'to': "orm['name.DraftSubmissionStateName']"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}), + 'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), + 'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'submit.submissionevent': { + 'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.idsubmissionstatus': { + 'Meta': {'object_name': 'IdSubmissionStatus'}, + 'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.tempidauthors': { + 'Meta': {'object_name': 'TempIdAuthors'}, + 'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'id_document_tag': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/migrations/0005_fill_in_new_fields.py b/ietf/submit/migrations/0005_fill_in_new_fields.py new file mode 100644 index 000000000..0c47a81c3 --- /dev/null +++ b/ietf/submit/migrations/0005_fill_in_new_fields.py @@ -0,0 +1,392 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + import hashlib, random, time + from django.conf import settings + + state_map = dict((n.slug, n) for n in orm["name.DraftSubmissionStateName"].objects.all()) + status_map = { + -4: "cancel", + -2: "posted", # Secretariat + -1: "posted", + 1: "uploaded", + 4: "auth", + 5: "manual", + 10: "grp-appr", + } + + from django.core.validators import validate_email, ValidationError + + for d in orm.Submission.objects.all().iterator(): + if not d.name: + # get rid of a few mishaps that seem to have been + # accepted without a name + d.delete() + continue + + # map state + state = state_map[status_map.get(d.status_id, "cancel")] + + # map authors + authors = [] + submitter = "" + submitter_email = "" + + for a in d.tempidauthors_set.order_by("author_order"): + parts = (a.first_name or '', a.middle_initial or '', a.last_name or '', a.name_suffix or '') + name = u" ".join(x.strip() for x in parts if x.strip()) + email = a.email_address + orig = email + + # clean + name = name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip() + email = email.replace("Email:", "").replace("E-mail:", "").replace("mailto:", "").replace(">", "").replace("<", "").replace("\n", "").replace(" ", "").rstrip(",").lstrip(":").strip(".").rstrip(";").rstrip("-").rstrip("\"").lstrip("\"").rstrip("@") + + if email: + line = u"%s <%s>" % (name, email) + else: + line = name + + if a.author_order == 0: + submitter = line + submitter_email = email + else: + authors.append(line) + + # make sure we always have a key + access_key = d.access_key + if not access_key: + access_key = hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random()) + str(d.name.encode("utf-8"))).hexdigest()[:32] + + + # fill in submission event + submitter_person = None + if d.submitter_tag: + try: + submitter_person = orm["person.Person"].objects.get(id=d.submitter_tag) + except models.ObjectDoesNotExist: + pass + + if submitter_email: + try: + submitter_person = orm["person.Person"].objects.get(email__address=submitter_email) + except models.ObjectDoesNotExist: + pass + + if submitter_person and not submitter: + submitter = submitter_person.name + + if submitter_person: + orm.SubmissionEvent.objects.get_or_create( + submission=d, + time=d.submission_date, + by=submitter_person, + desc="Uploaded submission", + ) + + + # fill in manual post events + if d.status_id == -2 and d.man_posted_by: + if d.man_posted_by == "Amy Vezza": + d.man_posted_by = "Amy K. Vezza" + + try: + by = orm["person.Person"].objects.get(name=d.man_posted_by) + orm.SubmissionEvent.objects.get_or_create( + submission=d, + time=d.man_posted_date or d.submission_date, + by=by, + desc="Posted submission manually", + ) + except models.ObjectDoesNotExist: + pass + + # update the new revision doc events that are set to + # "(System)" with our newly discovered submitter + if submitter_person and d.state_id == "posted": + orm["doc.NewRevisionDocEvent"].objects.filter(doc=d.name, rev=d.rev, type="new_revision", by=0).update(by=submitter_person) + + # update the submission itself + orm.Submission.objects.filter(pk=d.pk).update( + submitter=submitter, + state=state, + authors="\n".join(authors), + access_key=access_key, + ) + + + def backwards(self, orm): + "Write your backwards methods here." + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.docevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'doc.newrevisiondocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + 'submit.submission': { + 'Meta': {'object_name': 'Submission'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'default': "'uploaded'", 'to': "orm['name.DraftSubmissionStateName']"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}), + 'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), + 'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'submit.submissionevent': { + 'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.idsubmissionstatus': { + 'Meta': {'object_name': 'IdSubmissionStatus'}, + 'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.tempidauthors': { + 'Meta': {'object_name': 'TempIdAuthors'}, + 'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'id_document_tag': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/migrations/0006_auto__del_tempidauthors__del_idsubmissionstatus__del_field_submission_.py b/ietf/submit/migrations/0006_auto__del_tempidauthors__del_idsubmissionstatus__del_field_submission_.py new file mode 100644 index 000000000..7269d3c93 --- /dev/null +++ b/ietf/submit/migrations/0006_auto__del_tempidauthors__del_idsubmissionstatus__del_field_submission_.py @@ -0,0 +1,301 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting model 'TempIdAuthors' + db.delete_table('submit_tempidauthors') + + # Deleting model 'IdSubmissionStatus' + db.delete_table('submit_idsubmissionstatus') + + # Deleting field 'Submission.man_posted_date' + db.delete_column('submit_submission', 'man_posted_date') + + # Deleting field 'Submission.status' + db.delete_column('submit_submission', 'status_id') + + # Deleting field 'Submission.submitter_tag' + db.delete_column('submit_submission', 'submitter_tag') + + # Deleting field 'Submission.sub_email_priority' + db.delete_column('submit_submission', 'sub_email_priority') + + # Deleting field 'Submission.man_posted_by' + db.delete_column('submit_submission', 'man_posted_by') + + + def backwards(self, orm): + + # Adding model 'TempIdAuthors' + db.create_table('submit_tempidauthors', ( + ('last_name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('last_modified_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), + ('id_document_tag', self.gf('django.db.models.fields.IntegerField')(default=-1)), + ('last_modified_time', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('email_address', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('middle_initial', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('first_name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('submission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.Submission'])), + ('name_suffix', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('author_order', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + )) + db.send_create_signal('submit', ['TempIdAuthors']) + + # Adding model 'IdSubmissionStatus' + db.create_table('submit_idsubmissionstatus', ( + ('status_value', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('status_id', self.gf('django.db.models.fields.IntegerField')(primary_key=True)), + )) + db.send_create_signal('submit', ['IdSubmissionStatus']) + + # Adding field 'Submission.man_posted_date' + db.add_column('submit_submission', 'man_posted_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default=False) + + # Adding field 'Submission.status' + db.add_column('submit_submission', 'status', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.IdSubmissionStatus'], null=True, db_column='status_id', blank=True), keep_default=False) + + # Adding field 'Submission.submitter_tag' + db.add_column('submit_submission', 'submitter_tag', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False) + + # Adding field 'Submission.sub_email_priority' + db.add_column('submit_submission', 'sub_email_priority', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False) + + # Adding field 'Submission.man_posted_by' + db.add_column('submit_submission', 'man_posted_by', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['name.DraftSubmissionStateName']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.submission': { + 'Meta': {'object_name': 'Submission'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DraftSubmissionStateName']"}), + 'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), + 'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.submissionevent': { + 'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/models.py b/ietf/submit/models.py index d5558b8f7..d523f88fb 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -5,64 +5,73 @@ from django.db import models from ietf.person.models import Person from ietf.group.models import Group +from ietf.name.models import DraftSubmissionStateName +from ietf.utils.uniquekey import generate_unique_key -class IdSubmissionStatus(models.Model): - status_id = models.IntegerField(primary_key=True) - status_value = models.CharField(blank=True, max_length=255) +def parse_email_line(line): + """Split line on the form 'Some Name '""" + m = re.match("([^<]+) <([^>]+)>$", line) + if m: + return dict(name=m.group(1), email=m.group(2)) + else: + return dict(name=line, email="") + +class Submission(models.Model): + state = models.ForeignKey(DraftSubmissionStateName) + remote_ip = models.CharField(max_length=100, blank=True) + + access_key = models.CharField(max_length=255, default=generate_unique_key) + auth_key = models.CharField(max_length=255, blank=True) + + # draft metadata + name = models.CharField(max_length=255, db_index=True) + group = models.ForeignKey(Group, null=True, blank=True) + title = models.CharField(max_length=255, blank=True) + abstract = models.TextField(blank=True) + rev = models.CharField(max_length=3, blank=True) + pages = models.IntegerField(null=True, blank=True) + authors = models.TextField(blank=True, help_text="List of author names and emails, one author per line, e.g. \"John Doe <john@example.org>\"") + note = models.TextField(blank=True) + replaces = models.CharField(max_length=255, blank=True) + + first_two_pages = models.TextField(blank=True) + file_types = models.CharField(max_length=50, blank=True) + file_size = models.IntegerField(null=True, blank=True) + document_date = models.DateField(null=True, blank=True) + submission_date = models.DateField(default=datetime.date.today) + + submitter = models.CharField(max_length=255, blank=True, help_text="Name and email of submitter, e.g. \"John Doe <john@example.org>\"") + + idnits_message = models.TextField(blank=True) def __unicode__(self): - return self.status_value + return u"%s-%s" % (self.name, self.rev) -class IdSubmissionDetail(models.Model): - submission_id = models.AutoField(primary_key=True) - temp_id_document_tag = models.IntegerField(null=True, blank=True) - status = models.ForeignKey(IdSubmissionStatus, db_column='status_id', null=True, blank=True) - last_updated_date = models.DateField(null=True, blank=True) - last_updated_time = models.CharField(null=True, blank=True, max_length=25) - id_document_name = models.CharField(null=True, blank=True, max_length=255) - group_acronym = models.ForeignKey(Group, null=True, blank=True) - filename = models.CharField(null=True, blank=True, max_length=255, db_index=True) - creation_date = models.DateField(null=True, blank=True) - submission_date = models.DateField(null=True, blank=True) - remote_ip = models.CharField(null=True, blank=True, max_length=100) - revision = models.CharField(null=True, blank=True, max_length=3) - submitter_tag = models.IntegerField(null=True, blank=True) - auth_key = models.CharField(null=True, blank=True, max_length=255) - idnits_message = models.TextField(null=True, blank=True) - file_type = models.CharField(null=True, blank=True, max_length=50) - comment_to_sec = models.TextField(null=True, blank=True) - abstract = models.TextField(null=True, blank=True) - txt_page_count = models.IntegerField(null=True, blank=True) - error_message = models.CharField(null=True, blank=True, max_length=255) - warning_message = models.TextField(null=True, blank=True) - wg_submission = models.IntegerField(null=True, blank=True) - filesize = models.IntegerField(null=True, blank=True) - man_posted_date = models.DateField(null=True, blank=True) - man_posted_by = models.CharField(null=True, blank=True, max_length=255) - first_two_pages = models.TextField(null=True, blank=True) - sub_email_priority = models.IntegerField(null=True, blank=True) - invalid_version = models.IntegerField(null=True, blank=True) - idnits_failed = models.IntegerField(null=True, blank=True) - submission_hash = models.CharField(null=True, blank=True, max_length=255) - replaces = models.CharField(null=True, blank=True, max_length=255) + def authors_parsed(self): + res = [] + for line in self.authors.replace("\r", "").split("\n"): + line = line.strip() + if line: + res.append(parse_email_line(line)) + return res + + def submitter_parsed(self): + return parse_email_line(self.submitter) + + +class SubmissionEvent(models.Model): + submission = models.ForeignKey(Submission) + time = models.DateTimeField(default=datetime.datetime.now) + by = models.ForeignKey(Person, null=True, blank=True) + desc = models.TextField() def __unicode__(self): - return u"%s-%s" % (self.filename, self.revision) + return u"%s %s by %s at %s" % (self.submission.name, self.desc, self.by.plain_name() if self.by else "(unknown)", self.time) - def create_hash(self): - self.submission_hash = hashlib.md5(settings.SECRET_KEY + self.filename).hexdigest() + class Meta: + ordering = ("-time", "-id") - def get_hash(self): - if not self.submission_hash: - self.create_hash() - self.save() - return self.submission_hash - -def create_submission_hash(sender, instance, **kwargs): - instance.create_hash() - -models.signals.pre_save.connect(create_submission_hash, sender=IdSubmissionDetail) class Preapproval(models.Model): """Pre-approved draft submission name.""" @@ -72,25 +81,3 @@ class Preapproval(models.Model): def __unicode__(self): return self.name - -class TempIdAuthors(models.Model): - id_document_tag = models.IntegerField() - first_name = models.CharField(blank=True, max_length=255) # with new schema, this contains the full name while the other name fields are empty to avoid loss of information - last_name = models.CharField(blank=True, max_length=255) - email_address = models.CharField(blank=True, max_length=255) - last_modified_date = models.DateField(null=True, blank=True) - last_modified_time = models.CharField(blank=True, max_length=100) - author_order = models.IntegerField(null=True, blank=True) - submission = models.ForeignKey(IdSubmissionDetail) - middle_initial = models.CharField(blank=True, max_length=255, null=True) - name_suffix = models.CharField(blank=True, max_length=255, null=True) - - def email(self): - return (self.get_full_name(), self.email_address) - - def get_full_name(self): - parts = (self.first_name or '', self.middle_initial or '', self.last_name or '', self.name_suffix or '') - return u" ".join(x.strip() for x in parts if x.strip()) - - def __unicode__(self): - return u"%s <%s>" % self.email() diff --git a/ietf/submit/parsers/base.py b/ietf/submit/parsers/base.py index 7dd8618ae..53c0afa38 100644 --- a/ietf/submit/parsers/base.py +++ b/ietf/submit/parsers/base.py @@ -1,26 +1,28 @@ import re - -CUTOFF_HOUR = 17 - - -class MetaDataDraft(object): - revision = None - filename = None +class MetaData(object): + rev = None + name = None group = None - filesize = None + file_size = None first_two_pages = None - page_count = None + pages = None submission_date = None - creation_date = None + document_date = None authors = None class ParseInfo(object): + """Collect errors from a parse""" def __init__(self): self.errors = [] + # warnings are currently unused by the parsers self.warnings = {} - self.metadraft = MetaDataDraft() + # the metadata fields are currently unused, i.e. the plain + # text parser fills in some fields but they are not used + # anywhere (instead the draft parser is used for .txt and the + # other file types have no actual parsing at the moment) + self.metadata = MetaData() def add_error(self, error_str): self.errors.append(error_str) diff --git a/ietf/submit/parsers/plain_parser.py b/ietf/submit/parsers/plain_parser.py index cb9c52490..fb1e4c723 100644 --- a/ietf/submit/parsers/plain_parser.py +++ b/ietf/submit/parsers/plain_parser.py @@ -17,14 +17,14 @@ class PlainParser(FileParser): super(PlainParser, self).critical_parse() self.parse_max_size() self.parse_file_charset() - self.parse_filename() + self.parse_name() return self.parsed_info def parse_max_size(self): - if self.fd.size > settings.MAX_PLAIN_DRAFT_SIZE: - self.parsed_info.add_error('File size is larger than %s' % filesizeformat(settings.MAX_PLAIN_DRAFT_SIZE)) - self.parsed_info.metadraft.filesize = self.fd.size - self.parsed_info.metadraft.submission_date = datetime.date.today() + if self.fd.size > settings.IDSUBMIT_MAX_PLAIN_DRAFT_SIZE: + self.parsed_info.add_error('File size is larger than %s' % filesizeformat(settings.IDSUBMIT_MAX_PLAIN_DRAFT_SIZE)) + self.parsed_info.metadata.file_size = self.fd.size + self.parsed_info.metadata.submission_date = datetime.date.today() def parse_file_charset(self): import magic @@ -40,9 +40,9 @@ class PlainParser(FileParser): magic.magic_load(m.cookie, None) filetype = m.from_buffer(content) if not 'ascii' in filetype: - self.parsed_info.add_error('A plain text document must be submitted.') + self.parsed_info.add_error('A plain text ASCII document must be submitted.') - def parse_filename(self): + def parse_name(self): self.fd.file.seek(0) draftre = re.compile('(draft-\S+)') revisionre = re.compile('.*-(\d+)$') @@ -53,24 +53,24 @@ class PlainParser(FileParser): match = draftre.search(line) if not match: continue - filename = match.group(1) - filename = re.sub('^[^\w]+', '', filename) - filename = re.sub('[^\w]+$', '', filename) - filename = re.sub('\.txt$', '', filename) - extra_chars = re.sub('[0-9a-z\-]', '', filename) + name = match.group(1) + name = re.sub('^[^\w]+', '', name) + name = re.sub('[^\w]+$', '', name) + name = re.sub('\.txt$', '', name) + extra_chars = re.sub('[0-9a-z\-]', '', name) if extra_chars: if len(extra_chars) == 1: - self.parsed_info.add_error((u'The filename contains a disallowed character: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + + self.parsed_info.add_error((u'The name contains a disallowed character: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + u'(see http://www.ietf.org/id-info/guidelines.html#naming for details).') else: - self.parsed_info.add_error((u'The filename contains disallowed characters: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + + self.parsed_info.add_error((u'The name contains disallowed characters: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + u'(see http://www.ietf.org/id-info/guidelines.html#naming for details).') - match_revision = revisionre.match(filename) + match_revision = revisionre.match(name) if match_revision: - self.parsed_info.metadraft.revision = match_revision.group(1) + self.parsed_info.metadata.rev = match_revision.group(1) else: - self.parsed_info.add_error(u'The filename found on the first page of the document does not contain a revision: "%s"' % (filename,)) - filename = re.sub('-\d+$', '', filename) - self.parsed_info.metadraft.filename = filename + self.parsed_info.add_error(u'The name found on the first page of the document does not contain a revision: "%s"' % (name,)) + name = re.sub('-\d+$', '', name) + self.parsed_info.metadata.name = name return - self.parsed_info.add_error('The first page of the document does not contain a legitimate filename that start with draft-*') + self.parsed_info.add_error('The first page of the document does not contain a legitimate name that start with draft-*') diff --git a/ietf/submit/templatetags/submit_tags.py b/ietf/submit/templatetags/submit_tags.py index e86142050..5692db021 100644 --- a/ietf/submit/templatetags/submit_tags.py +++ b/ietf/submit/templatetags/submit_tags.py @@ -4,40 +4,37 @@ from django import template from django.conf import settings from django.utils.html import mark_safe, escape -from ietf.submit.utils import POSTED, POSTED_BY_SECRETARIAT - - register = template.Library() @register.inclusion_tag('submit/submission_files.html', takes_context=True) def show_submission_files(context, submission): result = [] - for ext in submission.file_type.split(','): + for ext in submission.file_types.split(','): exists = False - source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext)) + source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext)) if os.path.exists(source): exists = True - elif submission.status_id in [POSTED, POSTED_BY_SECRETARIAT]: + elif submission.state_id == "posted": continue - result.append({'name': '[%s version ]' % ext[1:].capitalize(), + result.append({'name': '[%s version]' % ext[1:].upper(), 'exists': exists, - 'url': '%s%s-%s%s' % (settings.IDSUBMIT_STAGING_URL, submission.filename, submission.revision, ext)}) + 'url': '%s%s-%s%s' % (settings.IDSUBMIT_STAGING_URL, submission.name, submission.rev, ext)}) return {'files': result} @register.filter -def two_pages_decorated_with_validation(value, validation): - pages = value.first_two_pages or '' - if not 'revision' in validation.warnings.keys(): - return mark_safe('' % escape(pages)) - result = '
    • -
    • Submit a draft
    • +
    • Submit a draft
    • {% if user|has_role:"WG Chair" %}
    • Approve a draft
    • {% endif %} diff --git a/ietf/templates/submit/announce_new_version.txt b/ietf/templates/submit/announce_new_version.txt index 4ff47877b..79821349d 100644 --- a/ietf/templates/submit/announce_new_version.txt +++ b/ietf/templates/submit/announce_new_version.txt @@ -1,15 +1,15 @@ {% autoescape off %} -A new version (-{{ submission.revision }}) has been submitted for {{ submission.filename }}: -http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt +A new version (-{{ submission.rev }}) has been submitted for {{ submission.name }}: +http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt {% if msg %} {{ msg|striptags }} {% endif %} The IETF datatracker page for this Internet-Draft is: -https://datatracker.ietf.org/doc/{{ submission.filename }}/ +https://datatracker.ietf.org/doc/{{ submission.name }}/ Diff from previous version: -http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }} +http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }} Please note that it may take a couple of minutes from the time of submission until the htmlized version and diff are available at tools.ietf.org. diff --git a/ietf/templates/submit/announce_to_authors.txt b/ietf/templates/submit/announce_to_authors.txt index 5f1e8c61d..1d61b9c86 100644 --- a/ietf/templates/submit/announce_to_authors.txt +++ b/ietf/templates/submit/announce_to_authors.txt @@ -1,23 +1,23 @@ {% autoescape off %} -A new version of I-D, {{ submission.filename }}-{{ submission.revision }}.txt -has been successfully submitted by {{ submitter }} and posted to the +A new version of I-D, {{ submission.name }}-{{ submission.rev }}.txt +has been successfully submitted by {{ submission.submitter_parsed.name }} and posted to the IETF repository. -Filename: {{ submission.filename }} -Revision: {{ submission.revision }} -Title: {{ submission.id_document_name }} -Creation date: {{ submission.creation_date|date:"Y-m-d" }} -Group: {{ wg }} -Number of pages: {{ submission.txt_page_count }} -URL: http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt -Status: http://datatracker.ietf.org/doc/{{ submission.filename }} -Htmlized: http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }} -{% ifnotequal submission.revision "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }}{% endifnotequal %} +Name: {{ submission.name }} +Revision: {{ submission.rev }} +Title: {{ submission.title }} +Document date: {{ submission.document_date|date:"Y-m-d" }} +Group: {{ group }} +Pages: {{ submission.pages }} +URL: http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt +Status: https://datatracker.ietf.org/doc/{{ submission.name }}/ +Htmlized: http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }} +{% if submission.rev != "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }}{% endif %} Abstract: {{ submission.abstract }} -{{ submission.comment_to_sec|default:"" }} +{{ submission.note|default:"" }} Please note that it may take a couple of minutes from the time of submission until the htmlized version and diff are available at tools.ietf.org. diff --git a/ietf/templates/submit/announce_to_lists.txt b/ietf/templates/submit/announce_to_lists.txt index 0c195c5cd..eb4324bda 100644 --- a/ietf/templates/submit/announce_to_lists.txt +++ b/ietf/templates/submit/announce_to_lists.txt @@ -1,25 +1,25 @@ {% autoescape off %} A New Internet-Draft is available from the on-line Internet-Drafts directories. -{% if submission.group_acronym %} This draft is a work item of the {{ submission.group_acronym.name }} Working Group of the IETF.{% endif %} +{% if submission.group %} This draft is a work item of the {{ submission.group.name }} Working Group of the IETF.{% endif %} - Title : {{ submission.id_document_name }} - Author(s) : {% for author in authors %}{{ author }}{% if not forloop.last %} + Title : {{ submission.title }} + Author{{ submission.authors_parsed|pluralize:" ,s" }} : {% for author in submission.authors_parsed %}{{ author.name }}{% if not forloop.last %} {% endif %}{% endfor %} - Filename : {{ submission.filename }}-{{ submission.revision }}.txt - Pages : {{ submission.txt_page_count }} + Filename : {{ submission.name }}-{{ submission.rev }}.txt + Pages : {{ submission.pages }} Date : {{ submission.submission_date|date:"Y-m-d" }} Abstract: {{ submission.abstract }} The IETF datatracker status page for this draft is: -https://datatracker.ietf.org/doc/{{ submission.filename }} +https://datatracker.ietf.org/doc/{{ submission.name }}/ There's also a htmlized version available at: -http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }} -{% if submission.revision != "00" %} +http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }} +{% if submission.rev != "00" %} A diff from the previous version is available at: -http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.filename }}-{{ submission.revision }} +http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.name }}-{{ submission.rev }} {% endif %} Please note that it may take a couple of minutes from the time of submission diff --git a/ietf/templates/submit/approval_request.txt b/ietf/templates/submit/approval_request.txt new file mode 100644 index 000000000..46e647cd7 --- /dev/null +++ b/ietf/templates/submit/approval_request.txt @@ -0,0 +1,33 @@ +{% autoescape off %} +Hi, + +Chair approval is needed for posting of {{ submission.name }}-{{ submission.rev }}. + +To approve the draft, go to this URL (note: you need to login to be able to approve): + https://{{ domain }}/submit/status/{{ submission.pk }}/{{ submission.access_key }}/ + + File name : {{ submission.name }} + Revision : {{ submission.rev }} + Submission date : {{ submission.submission_date }} + Group : {{ submission.group|default:"Individual Submission" }} + + Title : {{ submission.title }} + Document date : {{ submission.document_date }} + Pages : {{ submission.pages }} + File size : {{ submission.file_size|filesizeformat }} + + Submitter : {{ submission.submitter }} + + Abstract : {{ submission.abstract }} + + + Authors: +{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%} +{% endfor %} +{% endautoescape %} + + +Best regards, + + The IETF Secretariat + through the draft submission service diff --git a/ietf/templates/submit/approvals.html b/ietf/templates/submit/approvals.html index b4907d139..cfecabd85 100644 --- a/ietf/templates/submit/approvals.html +++ b/ietf/templates/submit/approvals.html @@ -22,16 +22,16 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; }

      You don't have any submissions to approve.

      {% else %} - - - - -{% for d in approvals %} - - - - -{% endfor %} + + + + + {% for s in approvals %} + + + + + {% endfor %}
      SubmissionSubmitted
      {{ d.filename }}-{{ d.revision }}{{ d.submission_date }}
      SubmissionSubmitted
      {{ s.name }}-{{ s.rev }}{{ s.submission_date }}
      {% endif %} @@ -46,19 +46,19 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; } {% else %} - - - - - -{% for p in preapprovals %} - - - - - - -{% endfor %} + + + + + + {% for p in preapprovals %} + + + + + + + {% endfor %}
      Draft namePre-approvedBy
      {{ p.name }}{{ p.time|date:"Y-m-d" }}{{ p.by }}cancel pre-approval
      Draft namePre-approvedBy
      {{ p.name }}{{ p.time|date:"Y-m-d" }}{{ p.by }}cancel pre-approval
      {% endif %} @@ -69,18 +69,19 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; } {% else %} - - - - -{% for d in recently_approved %} - - - - -{% endfor %} + + + + + {% for d in recently_approved %} + + + + + {% endfor %}
      DraftSubmitted
      {{ d.filename }}{{ d.submission_date }}
      DraftSubmitted
      {{ d.name }}{{ d.submission_date }}
      {% endif %} + {% else %}

      Submission approvals

      diff --git a/ietf/templates/submit/confirm_submission.html b/ietf/templates/submit/confirm_submission.html new file mode 100644 index 000000000..8c0e4b56b --- /dev/null +++ b/ietf/templates/submit/confirm_submission.html @@ -0,0 +1,56 @@ +{% extends "submit/submit_base.html" %} + +{% block title %}Confirm submission of {{ submission.name }}{% endblock %} + +{% block morecss %} +{{ block.super }} +p.error { color: red; font-weight: bold; font-size: 1.5em; } +{% endblock %} + + +{% block submit_content %} + +

      Confirm submission of {{ submission.name }}

      + +{% if submission.state_id != "auth" and submission.state_id != "aut-appr" %} + {% if submission.state_id == "posted" %} +

      The submission has already been posted. See the draft here.

      + {% else %} +

      The submission is not in a state where it can be confirmed.

      + +

      Go to the status page + to see what has happened to it.

      + {% endif %} +{% else %} + + {% if auth_key != submission.auth_key %} +

      Incorrect authorization key.

      + +

      Double-check the link you followed. If everything fails, you can go to + the status page, + cancel the submission and try again.

      + {% else %} +

      Authorization key accepted.

      + +

      Please press the button below to finish posting of + {{ submission.name }}-{{ submission.rev }}

      + +
      + +
      + {% endif %} + +{% endif %} + +{% endblock %} + +{% block scripts %} +jQuery(function () { + jQuery("form").submit(function() { + if (this.submittedAlready) + return false; + else + this.submittedAlready = true; + }); +}); +{% endblock %} diff --git a/ietf/templates/submit/confirm_autopost.txt b/ietf/templates/submit/confirm_submission.txt similarity index 77% rename from ietf/templates/submit/confirm_autopost.txt rename to ietf/templates/submit/confirm_submission.txt index d2b586309..0aa84db00 100644 --- a/ietf/templates/submit/confirm_autopost.txt +++ b/ietf/templates/submit/confirm_submission.txt @@ -2,12 +2,13 @@ Hi, The IETF datatracker draft submission service has received your draft -{{ draft.filename }}-{{ draft.revision }}, and requires a +{{ submission.name }}-{{ submission.rev }}, and requires a confirmation step in order to be able to complete the posting of the draft. Please follow this link to the page where you can confirm the posting: -Confirmation URL: {{ confirm_url|safe }} + +{{ confirm_url }} Best regards, diff --git a/ietf/templates/submit/draft_edit.html b/ietf/templates/submit/draft_edit.html deleted file mode 100644 index a1d865374..000000000 --- a/ietf/templates/submit/draft_edit.html +++ /dev/null @@ -1,169 +0,0 @@ -{% extends "submit/draft_status.html" %} -{% load submit_tags %} -{% block title %}Adjust Meta-Data{% endblock %} - -{% block morecss %} -{{ block.super }} -table.metadata-table #id_title, table.metadata-table #id_abstract, table.metadata-table #id_comments { width: 500px; } -table.metadata-table tr.warning th, table.metadata-table tr.warning td { background-color: #ffeebb; } -table.ietf-table tr { vertical-align: top; } -table.ietf-table tr.error { background-color: #ffeebb; border-top: 1px dashed red; border-bottom: 1px dashed red;} -table.ietf-table span.field-error { display: block; color: red; } -{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock %} - -{% block submit_content %} -

      Adjust External Meta-Data

      - - - - - - - - - - -

      Adjust data

      -{% if form.errors %} - -{% endif %} -
      - - - - - - - - -{% if settings.USE_DB_REDESIGN_PROXY_CLASSES %} - -{% else %} - - -{% endif %} - - - - -

      Authors

      - - - - {% if settings.USE_DB_REDESIGN_PROXY_CLASSES %} - - {% else %} - - - {% endif %} - - - - -{% for author in form.get_authors %} - - {% if settings.USE_DB_REDESIGN_PROXY_CLASSES %} - - {% else %} - - - {% endif %} - - -{% endfor %} - -
      NameFirst nameLast nameEmail address
      {{ author.get_full_name|default:"" }}{{ author.errors.name }}{{ author.first_name|default:"" }}{{ author.errors.first_name }}{{ author.last_name|default:"" }}{{ author.errors.last_name }}{{ author.email.1|default:"" }}{{ author.errors.email }}
      -
      - -
      - -
      - -

      -The IETF is an organized activity of the Internet Society -
      Please send problem reports to ietf-action@ietf.org. -

      -{% endblock %} diff --git a/ietf/templates/submit/draft_status.html b/ietf/templates/submit/draft_status.html deleted file mode 100644 index 9705ac990..000000000 --- a/ietf/templates/submit/draft_status.html +++ /dev/null @@ -1,257 +0,0 @@ -{% extends "submit/submit_base.html" %} -{% load submit_tags %} -{% block title %}Submission status{% endblock %} - -{% block morecss %} -{{ block.super }} -div.metadata-errors { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; } -div.info-message-error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; color: red; } -div.info-message-success { border: 1px solid green; background-color: #eeffbb; padding: 5px 10px; margin: 1em 0px; color: green; } -table.metadata-table th { white-space: nowrap; font-weight: bold; } -table.metadata-table #id_first_name, table.metadata-table #id_last_name { width: 200px; } -table.metadata-table #id_email { width: 400px; } -table.metadata-table th, table.metadata-table td { text-align: left; background: #ddddff; padding: 5px 10px; } -table.metadata-table th.author { text-align: right; } -table.metadata-table tr { vertical-align: top; } -table.metadata-table tr.warning td, table.metadata-table tr.warning th { background-color: #ffaaaa; } -table.metadata-table div.warn_message { color: red; } -table.metadata-table ul.errorlist { color: red; padding: 0px; margin: 0px; list-style-type: none; } -pre.twopages { margin: 0px; } -{% endblock morecss %} - -{% block pagehead %} - - -{% if can_cancel %} - -{% endif %} - - -{% endblock %} - -{% block submit_content %} - -{% if status %} -

      Status of the submission: {{ status.status_value }}

      -{% endif %} - -{% if message %} -
      {{ message.1 }}
      -{% endif %} - -{% if auto_post_form.errors %} -
      Please fix errors in the form below
      -{% endif %} - -

      Check Page

      -

      -{% if validation.passes_idnits %} -Your draft has been verified to meet IDNITS requirements. -{% else %} -Your draft has NOT been verified to meet IDNITS requirements. -{% endif %} -(View IDNITS Results) -

      - - - -

      Meta-Data from the Draft

      -{% if validation.warnings %} - -{% endif %} - - - - - - - - - - - - -{% if validation.warnings.authors %} - -{% endif %} -{% if validation.authors %} -{% for author in validation.authors %} - -{% endfor %} -{% endif %} - - - - - -{% if allow_edit %} -
      - (Leads to manual post by the Secretariat) -
      - - {% if is_valid %} -

      Please edit the following meta-data before proceeding to Auto-Post

      -

      - If you are one of the authors of this document, then please click the button with your name on it to automatically fill in the submitter information as requested below. Otherwise, please manually enter your information. -

      -
      - {{ auto_post_form.get_author_buttons|safe }} - - {{ auto_post_form }} - - -
      - {% endif %} - - -{% else %} - {% if validation.submitter %} -

      Submitter information

      - - - - - {% endif %} -{% endif %} - -{% if can_cancel %} -

      Cancel submission

      -

      -

      -
      -
      - This submission will be canceled, and its uploaded document(s) permanently deleted. -

      -{% endif %} - -{% if can_approve %} -

      -

      - -
      -

      -{% endif %} - -{% if can_force_post %} -

      -

      - -
      -

      -{% endif %} - -{% if show_notify_button %} - -{% endif %} -

      -The IETF is an organized activity of the Internet Society -
      Please send problem reports to ietf-action@ietf.org. -

      -{% endblock %} - -{% block scripts %} -jQuery(function () { - jQuery("form").submit(function() { - if (this.submittedAlready) - return false; - else - this.submittedAlready = true; - }); -}); -{% endblock %} diff --git a/ietf/templates/submit/edit_submission.html b/ietf/templates/submit/edit_submission.html new file mode 100644 index 000000000..13ba47316 --- /dev/null +++ b/ietf/templates/submit/edit_submission.html @@ -0,0 +1,114 @@ +{% extends "submit/submit_base.html" %} + +{% load submit_tags %} + +{% block title %}Adjust Meta-Data of Submitted {{ submission.name }}{% endblock %} + +{% block morecss %} +{{ block.super }} +table.metadata-table tr th { padding-right: 2em; } +table.metadata-table #id_edit-title, table.metadata-table #id_edit-abstract, table.metadata-table #id_edit-note { width: 40em; } +table.authors td { vertical-align: top; } +table.authors tr.empty { display: none; } +input.add-author { margin-left: 42em; } +div.manual-posting { margin-top: 2em; } +{% endblock %} + +{% block submit_content %} +

      Adjust Meta-Data of Submitted {{ submission.name }}

      + + + + + + + + + + + + + + + + + + + +

      Adjust meta-data

      + +{% if form_errors %} + +{% endif %} + +
      + + + {% for field in edit_form %} + {% if field.name != "note" %} + + {% endif %} + {% endfor %} + + {% include "submit/submitter_form.html" %} + + {% with edit_form.note as field %} + + {% endwith %} + + + +

      Authors

      + + + + + + + + + + + + + + {% for form in author_forms %} + + + + + {% endfor %} + + + +
      + +
      + +
      + +
      +
      + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} + +{% block js %} +{{ block.super }} + + +{% endblock %} diff --git a/ietf/templates/submit/request_full_url.txt b/ietf/templates/submit/full_url.txt similarity index 75% rename from ietf/templates/submit/request_full_url.txt rename to ietf/templates/submit/full_url.txt index ecb975303..a7df4d9f2 100644 --- a/ietf/templates/submit/request_full_url.txt +++ b/ietf/templates/submit/full_url.txt @@ -2,7 +2,7 @@ Hi, The datatracker has received a request to send out the link to the URL where you -can confirm the submission of your draft {{ submission.filename }}-{{ submission.revision }}. +can confirm the submission of your draft {{ submission.name }}-{{ submission.rev }}. Please follow this link to get full access to the submission page: {{ url|safe }} diff --git a/ietf/templates/submit/last_confirmation_step.html b/ietf/templates/submit/last_confirmation_step.html deleted file mode 100644 index 1b6ca01a4..000000000 --- a/ietf/templates/submit/last_confirmation_step.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "submit/submit_base.html" %} - -{% block title %}Confirm Auto-Post{% endblock %} - - -{% block submit_content %} - -

      Confirm auto-post

      -

      -Authorization key accepted. Please press the button below to finish Auto-Post of {{ detail.filename }}-{{ detail.revision }} -

      -
      - -
      -{% endblock %} - -{% block scripts %} -jQuery(function () { - jQuery("form").submit(function() { - if (this.submittedAlready) - return false; - else - this.submittedAlready = true; - }); -}); -{% endblock %} diff --git a/ietf/templates/submit/manual_post_mail.txt b/ietf/templates/submit/manual_post_mail.txt deleted file mode 100644 index 7fd5ffd9c..000000000 --- a/ietf/templates/submit/manual_post_mail.txt +++ /dev/null @@ -1,31 +0,0 @@ -{% autoescape off %} -Hi, - -Manual posting has been requested for the following Internet-Draft: - -I-D Submission Tool URL: - {{ url }} - - File name : {{ draft.filename }} - Version : {{ draft.revision }} - Submission date : {{ draft.submission_date }} - Group : {{ draft.group_acronym|default:"Individual Submission" }} {% if form.validation.warnings.group %}*Please note that this group is not an active one*{% endif %} - - Title : {{ draft.id_document_name }} - Document date : {{ draft.creation_date }} - Pages : {{ draft.txt_page_count }} - File size : {{ draft.filesize|filesizeformat }} - - Submitter : {{ submitter.get_full_name }} <{{ submitter.email.1 }}> - - Abstract : {{ draft.abstract }} - - - Authors: -{% for author in form.get_authors %} {{ author.get_full_name }} <{{ author.email.1 }}> -{% endfor %} - - Comments to the secretariat: - -{{ draft.comment_to_sec }} -{% endautoescape %} diff --git a/ietf/templates/submit/manual_post_request.txt b/ietf/templates/submit/manual_post_request.txt new file mode 100644 index 000000000..1620a476f --- /dev/null +++ b/ietf/templates/submit/manual_post_request.txt @@ -0,0 +1,31 @@ +{% autoescape off %} +Hi, + +Manual posting has been requested for the following Internet-Draft: + +I-D Submission Tool URL: + {{ url }} + + File name : {{ submission.name }} + Revision : {{ submission.rev }} + Submission date : {{ submission.submission_date }} + Group : {{ submission.group|default:"Individual Submission" }} {% if errors.group %}*Please note: {{ errors.group }}*{% endif %} + + Title : {{ submission.title }} + Document date : {{ submission.document_date }} + Pages : {{ submission.pages }} + File size : {{ submission.file_size|filesizeformat }} + + Submitter : {{ submission.submitter }} + + Abstract : {{ submission.abstract }} + + + Authors: +{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%} +{% endfor %} + + Comment to the secretariat: + +{{ submission.note }} +{% endautoescape %} diff --git a/ietf/templates/submit/problem-reports-footer.html b/ietf/templates/submit/problem-reports-footer.html new file mode 100644 index 000000000..8216223a5 --- /dev/null +++ b/ietf/templates/submit/problem-reports-footer.html @@ -0,0 +1,4 @@ + diff --git a/ietf/templates/submit/search_submission.html b/ietf/templates/submit/search_submission.html new file mode 100644 index 000000000..40131d6ac --- /dev/null +++ b/ietf/templates/submit/search_submission.html @@ -0,0 +1,18 @@ +{% extends "submit/submit_base.html" %} + +{% block title %}Submission status{% endblock %} + +{% block submit_content %} + +

      Please enter the name of the Internet-Draft you wish to view + submission status for:

      + +
      + {% if error %}
      {{ error }}
      {% endif %} + + +
      + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} diff --git a/ietf/templates/submit/submission_approval.txt b/ietf/templates/submit/submission_approval.txt deleted file mode 100644 index ddfa7fd94..000000000 --- a/ietf/templates/submit/submission_approval.txt +++ /dev/null @@ -1,33 +0,0 @@ -{% autoescape off %} -Hi, - -WG chair approval is needed for posting of {{ draft.filename }}-{{ draft.revision }}. - -To approve the draft, go to this URL (note: you need to login to be able to approve): - https://{{ domain }}/submit/status/{{ draft.submission_id }}/{{ draft.submission_hash }}/ - - File name : {{ draft.filename }} - Version : {{ draft.revision }} - Submission date : {{ draft.submission_date }} - Group : {{ draft.group_acronym|default:"Individual Submission" }} - - Title : {{ draft.id_document_name }} - Document date : {{ draft.creation_date }} - Pages : {{ draft.txt_page_count }} - File size : {{ draft.filesize|filesizeformat }} - - Submitter : {{ submitter.get_full_name }} <{{ submitter.email_address }}> - - Abstract : {{ draft.abstract }} - - - Authors: -{% for author in authors %} {{ author.get_full_name }} <{{ author.email.1 }}> -{% endfor %} -{% endautoescape %} - - -Best regards, - - The IETF Secretariat - through the draft submission service diff --git a/ietf/templates/submit/submission_status.html b/ietf/templates/submit/submission_status.html new file mode 100644 index 000000000..aceaaebf4 --- /dev/null +++ b/ietf/templates/submit/submission_status.html @@ -0,0 +1,275 @@ +{% extends "submit/submit_base.html" %} + +{% load ietf_filters submit_tags %} + +{% block title %}Status of submission of {{ submission.name }}-{{ submission.rev }}{% endblock %} + +{% block submit_content %} + +{% if submission.state_id != "uploaded" %} +

      Status of the submission: {{ submission.state.name }}

      +{% endif %} + +{% if message %} +
      {{ message.1 }}
      +{% endif %} + +{% if submitter_form.errors %} +
      Please fix errors in the form below
      +{% endif %} + +

      IDNITS

      +

      + {% if passes_idnits %} + Your draft has been verified to meet IDNITS requirements. + {% else %} + Your draft has NOT been verified to meet IDNITS requirements. + {% endif %} + (View IDNITS Results) +

      + + + + + + +

      Meta-Data from the Submission

      + +{% if errors %} + +{% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% for author in submission.authors_parsed %} + + + + + {% endfor %} + + + + + + + + + + + + + + + + + +{% if can_edit %} +
      + + (Leads to manual post by the Secretariat) +
      + + {% if passes_idnits and not errors %} +

      Please edit the following meta-data before posting

      + +

      + +
      + + {% include "submit/submitter_form.html" %} + + + + + {% if requires_group_approval %} + (Notifies group chairs to get approval) + {% else %} + {% if requires_prev_authors_approval %} + (Notifies authors of previous revision of draft to get approval) + {% else %} + (Notifies submitter and authors for confirmation) + {% endif %} + {% endif %} +
      + {% endif %} + +{% else %} + {% if submission.submitter %} +

      Submitter information

      + + + + + {% endif %} +{% endif %} + +{% if can_cancel %} +

      Cancel submission

      +

      + Cancel submission and delete the uploaded file{{ submission.file_types|split:","|pluralize }} permanently: + +

      + + +
      +

      +{% endif %} + +{% if can_group_approve %} +

      Approve submission

      +

      +

      + + +
      +

      +{% endif %} + +{% if can_force_post %} +

      +

      + + +
      +

      +{% endif %} + +{% if show_send_full_url %} + +{% endif %} + +

      History

      + + + + + {% for e in submission.submissionevent_set.all %} + + + + + + {% endfor %} +
      DateByText
      {{ e.time|date:"Y-m-d" }}{{ e.by|default:"" }}{{ e.desc }} +
      + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} + +{% block js %} + + +{% endblock %} diff --git a/ietf/templates/submit/submit_base.html b/ietf/templates/submit/submit_base.html index 984857bde..dc2cd05f2 100644 --- a/ietf/templates/submit/submit_base.html +++ b/ietf/templates/submit/submit_base.html @@ -1,34 +1,22 @@ {% extends "base.html" %} -{% block morecss %} -.ietf-navset { - background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px; - color:white; - border:1px solid black; - padding:4px; -} -.ietf-navset .selected { font-weight:bold; padding: 0 3px; } -.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } -.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; } +{% block pagehead %} +{{ block.super }} + {% endblock %} + {% block content %}

      IETF Internet-Draft Submission

      -{% if selected == "index" %}Upload{% else %}Upload{% endif %} | -{% if selected == "status" %}Status{% else %}Status{% endif %} | +{% if selected == "index" %}Upload{% else %}Upload{% endif %} | +{% if selected == "status" %}Status{% else %}Status{% endif %} | {% if selected == "instructions" %}Tool Instructions{% else %}Tool Instructions{% endif %} | {% if selected == "notewell" %}NOTE WELL{% else %}NOTE WELL{% endif %} | {% if selected == "approvals" %}Approvals{% else %}Approvals{% endif %}
      -{% if form.cutoff_warning %} -
      -{{ form.cutoff_warning|safe }} -
      -{% endif %} - {% block submit_content %} {% endblock %} {% endblock %} diff --git a/ietf/templates/submit/submit_index.html b/ietf/templates/submit/submit_index.html deleted file mode 100644 index bfde12714..000000000 --- a/ietf/templates/submit/submit_index.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "submit/submit_base.html" %} -{% block title %}Upload{% endblock %} - -{% block pagehead %} -{{ form.media }} -{% endblock %} - -{% block submit_content %} -

      This page is used to submit IETF Internet-Drafts to the Internet-Draft repository. The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt

      -

      Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts.

      -

      Internet-Drafts are draft documents, and are valid for a maximum of six months. They may be updated, replaced, or obsoleted by other documents at any time.

      -{% if not form.shutdown %} -

      If you run into problems when submitting an Internet-Draft using this and the following pages, you may alternatively submit your draft by email to internet-drafts@ietf.org. However, be advised that manual processing always takes additional time.

      - - {{ form }} -{% endif %} - -

      -The IETF is an organized activity of the Internet Society -
      Please send problem reports to ietf-action@ietf.org. -

      -{% endblock %} diff --git a/ietf/templates/submit/submit_status.html b/ietf/templates/submit/submit_status.html deleted file mode 100644 index e9a023125..000000000 --- a/ietf/templates/submit/submit_status.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "submit/submit_base.html" %} -{% block title %}Submission status{% endblock %} - -{% block pagehead %} -{{ form.media }} -{% endblock %} - -{% block submit_content %} -

      -Please enter the filename of the Internet-Draft you wish to view submission status for: -

      - -
      -{% if error %}
      {{ error }}
      {% endif %} - - -
      - -

      - -Note that the status page only displays the status of an Internet-Draft with a posting still in progress or an Internet-Draft that has been successfully posted. -

      -

      -The IETF is an organized activity of the Internet Society -
      Please send problem reports to ietf-action@ietf.org. -

      -{% endblock %} diff --git a/ietf/templates/submit/submitform.html b/ietf/templates/submit/submitform.html deleted file mode 100644 index 60816e359..000000000 --- a/ietf/templates/submit/submitform.html +++ /dev/null @@ -1,53 +0,0 @@ -{% load i18n %} - -
      - - - -
      - {% if form.errors %} -
      - Please correct the errors below. -
      - {{ form.non_field_errors }} - {% endif %} -{% for fieldset in form.get_fieldsets %} - {% if fieldset.name %} -
      -

      {{ fieldset.name }}

      - {% endif %} - - {% for field in fieldset.fields %} -
      - -
      -
      {{ field.help_text }}
      - {{ field }} - {{ field.errors }} -
      -
      -
      - {% endfor %} - - {% if fieldset.name %} -
      - {% endif %} -{% endfor %} -
      - -{% if not form.shutdown %} -
      - -
      -{% endif %} - -
      diff --git a/ietf/templates/submit/submitter_form.html b/ietf/templates/submit/submitter_form.html new file mode 100644 index 000000000..8dbb5c3b7 --- /dev/null +++ b/ietf/templates/submit/submitter_form.html @@ -0,0 +1,17 @@ + + Submitter + + If you are one of the authors, please click the button below + with your name on it to automatically fill in the + submitter information. Otherwise, + please manually enter your name and email address.

      + + {% for author in submission.authors_parsed %} + + {% endfor %} + + + + {% for field in submitter_form %} + {{ field.label_tag }}{{ field }}{{ field.errors }} + {% endfor %} diff --git a/ietf/templates/submit/tool_instructions.html b/ietf/templates/submit/tool_instructions.html index b01046972..fd32800ce 100644 --- a/ietf/templates/submit/tool_instructions.html +++ b/ietf/templates/submit/tool_instructions.html @@ -3,7 +3,7 @@ {% block submit_content %}

      I-D Submission Tool Instructions

      -

      Tool URL: http://datatracker.ietf.org/{% url submit_index %}

      +

      Tool URL: https://datatracker.ietf.org{% url submit_upload_submission %}

      This page will explain the purpose and content of each screen in the I-D Submission Tool, and the actions that result by clicking the form buttons on each screen.
      The specification for this tool can be found in RFC 4228. @@ -98,7 +98,7 @@ This is the screen where a user can adjust any meta-data that could have been in Status Screen

      -The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by filename. +The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by name.

      Form buttons and resulting actions:
      diff --git a/ietf/templates/submit/upload_submission.html b/ietf/templates/submit/upload_submission.html new file mode 100644 index 000000000..257547965 --- /dev/null +++ b/ietf/templates/submit/upload_submission.html @@ -0,0 +1,63 @@ +{% extends "submit/submit_base.html" %} +{% block title %}Upload{% endblock %} + +{% block morecss %} + {{ block.super }} + form.upload-form h3 { margin: 0; color: #fff; background-color: #2647a0; padding: 0.3em 0.8em; } + form.upload-form table { padding: 0.3em 0.8em; } + form.upload-form .required { color: #f00; } + form.upload-form td { padding-right: 5em; padding-top: 0.4em; padding-bottom: 0.4em; } + form.upload-form .ietf-box { margin-bottom: 1em; } +{% endblock %} + +{% block submit_content %} +{% if form.cutoff_warning %} +
      + {{ form.cutoff_warning|safe }} +
      +{% endif %} + +

      This page is used to submit IETF Internet-Drafts to the +Internet-Draft repository. The list of current Internet-Drafts can be +accessed athttp://www.ietf.org/ietf/1id-abstracts.txt

      + +

      Internet-Drafts are working documents of the Internet Engineering +Task Force (IETF), its areas, and its working groups. Note that other +groups may also distribute working documents as Internet-Drafts.

      + +

      Internet-Drafts are draft documents, and are valid for a maximum of +six months. They may be updated, replaced, or obsoleted by other +documents at any time.

      + +{% if not form.shutdown %} +

      If you run into problems when submitting an Internet-Draft +using this and the following pages, you may alternatively submit +your draft by email to +internet-drafts@ietf.org. +However, be advised that manual processing always takes additional time.

      + + +
      +

      Upload a draft

      + + {{ form.non_field_errors }} + +
      + {% for field in form %} + + + + + {% endfor %} +
      {{ field.label_tag }} {% if field.field.required %}*{% endif %}{{ field }} {{ field.errors }}
      +
      + +
      + +
      + +{% endif %} + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} diff --git a/static/css/submit.css b/static/css/submit.css new file mode 100644 index 000000000..cb6b7dbd4 --- /dev/null +++ b/static/css/submit.css @@ -0,0 +1,31 @@ +.ietf-navset { + background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px; + color:white; + border:1px solid black; + padding:4px; +} +.ietf-navset .selected { font-weight:bold; padding: 0 3px; } +.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } +.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; } +.problem-reports-footer { font-style: italic; margin-top: 2em; } + +div.metadata-errors { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; max-width: 50em; } +div.info-message-error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; color: red; } +div.info-message-success { border: 1px solid green; background-color: #eeffbb; padding: 5px 10px; margin: 1em 0px; color: green; } + +table.metadata-table th { white-space: nowrap; font-weight: bold; } +table.metadata-table th, table.metadata-table td { text-align: left; background: #ddddff; padding: 5px 10px; } +table.metadata-table th.author { text-align: right; } +table.metadata-table tr { vertical-align: top; } +table.metadata-table tr.error td, table.metadata-table tr.error th { background-color: #ffaaaa; } +table.metadata-table div.error-msg { color: red; } +table.metadata-table td.author-button-help { max-width: 40em; } +table.metadata-table input.name, table.metadata-table input.email { width: 25em; } + +pre.twopages { margin: 0px; } + +.idnits-popup .content, +.twopages-popup .content { background-color: #fff; width: 55em; height: 30em; overflow: auto; padding: 1em; } +a.idnits-trigger, a.twopages-trigger, a.idnits-trigger:visited, a.twopages-trigger:visited { color: #000; } + +table.history { max-width: 50em; } diff --git a/static/js/draft-submit.js b/static/js/draft-submit.js index fa9917143..0d51b657a 100644 --- a/static/js/draft-submit.js +++ b/static/js/draft-submit.js @@ -1,12 +1,60 @@ -$(function (){ +jQuery(function (){ // fill in submitter info when an author button is clicked - $("input[type=button]").click(function () { - var name = $(this).data("name"); - if (name == null) // backwards compatibility - return; - var email = $(this).data("email"); + jQuery("input[type=button].author").click(function () { + var name = jQuery(this).data("name"); + var email = jQuery(this).data("email"); - $(this).parents("form").find("input[name=name]").val(name || ""); - $(this).parents("form").find("input[name=email]").val(email || ""); + jQuery(this).parents("form").find("input[name=submitter-name]").val(name || ""); + jQuery(this).parents("form").find("input[name=submitter-email]").val(email || ""); + }); + + jQuery("form").submit(function() { + if (this.submittedAlready) + return false; + else { + this.submittedAlready = true; + return true; + } + }); + + jQuery("form#cancel-submission").submit(function () { + return confirm("Cancel this submission?"); + }); + + jQuery(".idnits-trigger").click(function (e) { + e.preventDefault(); + var popup = jQuery(".idnits-popup").clone().show(); + showModalBox(popup); + }); + + jQuery(".twopages-trigger").click(function (e) { + e.preventDefault(); + var popup = jQuery(".twopages-popup").clone().show(); + showModalBox(popup); + }); + + jQuery("form .add-author").click(function (e) { + e.preventDefault(); + + var table = jQuery("table.authors tbody"); + var row = table.find("tr.empty").clone(); + + row.removeClass("empty"); + var prefixInput = row.find('input[name=authors-prefix]'); + + // figure out a prefix + var i = 0, prefix; + do { + ++i; + prefix = prefixInput.val() + i; + } + while (table.find('input[name=authors-prefix][value="' + prefix +'"]').length > 0); + + prefixInput.val(prefix); + row.find('input').not(prefixInput).each(function () { + this.name = prefix + "-" + this.name; + }); + + table.append(row); }); }); From e98abbf56d8a48d7f30720661a2c98f9c317211c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 13:55:10 +0000 Subject: [PATCH 108/173] Change the port accepted for debug purposes to 2025 instead of 1025 to not conflict with the port on the live server - Legacy-Id: 6715 --- ietf/utils/mail.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ietf/utils/mail.py b/ietf/utils/mail.py index f53945825..669fda92b 100644 --- a/ietf/utils/mail.py +++ b/ietf/utils/mail.py @@ -177,10 +177,10 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F for k, v in extra.items(): if v: msg[k] = v - # start debug server with python -m smtpd -n -c DebuggingServer localhost:1025 + # start debug server with python -m smtpd -n -c DebuggingServer localhost:2025 # then put USING_DEBUG_EMAIL_SERVER=True and EMAIL_HOST='localhost' - # and EMAIL_PORT=1025 in settings_local.py - debugging = getattr(settings, "USING_DEBUG_EMAIL_SERVER", False) and settings.EMAIL_HOST == 'localhost' and settings.EMAIL_PORT == 1025 + # and EMAIL_PORT=2025 in settings_local.py + debugging = getattr(settings, "USING_DEBUG_EMAIL_SERVER", False) and settings.EMAIL_HOST == 'localhost' and settings.EMAIL_PORT == 2025 if test_mode or debugging or settings.SERVER_MODE == 'production': send_smtp(msg, bcc) From 6174e720360b85ad96658c5c4067810db5fc5093 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 16:07:10 +0000 Subject: [PATCH 109/173] Rename unique key to random key as it is not really unique, add function for generating an access token from the key - Legacy-Id: 6716 --- ietf/utils/accesstoken.py | 15 +++++++++++++++ ietf/utils/uniquekey.py | 7 ------- 2 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 ietf/utils/accesstoken.py delete mode 100644 ietf/utils/uniquekey.py diff --git a/ietf/utils/accesstoken.py b/ietf/utils/accesstoken.py new file mode 100644 index 000000000..da07c9a3a --- /dev/null +++ b/ietf/utils/accesstoken.py @@ -0,0 +1,15 @@ +import time, random, hashlib + +from django.conf import settings + +def generate_random_key(max_length=32): + """Generate a random access token.""" + return hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random())).hexdigest()[:max_length] + +def generate_access_token(key, max_length=32): + """Make an access token out of key.""" + assert key, "key must not be empty" + # we hash it with the private key to make sure only we can + # generate and use the final token - so storing the key in the + # database is safe + return hashlib.sha256(settings.SECRET_KEY + key).hexdigest()[:max_length] diff --git a/ietf/utils/uniquekey.py b/ietf/utils/uniquekey.py deleted file mode 100644 index 9adcf0db6..000000000 --- a/ietf/utils/uniquekey.py +++ /dev/null @@ -1,7 +0,0 @@ -import time, random, hashlib - -from django.conf import settings - -def generate_unique_key(max_length=32): - return hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random())).hexdigest()[:max_length] - From 650d8316eb0fd4d971df158d806b52e710c73a22 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 16:09:05 +0000 Subject: [PATCH 110/173] Make the submit tool use the access token framework with a bit of backwards compatibility glue - Legacy-Id: 6717 --- ietf/submit/admin.py | 2 +- ietf/submit/mail.py | 10 ++-- ietf/submit/models.py | 7 ++- ietf/submit/urls.py | 6 +-- ietf/submit/views.py | 46 +++++++++++-------- ietf/templates/submit/approval_request.txt | 2 +- ietf/templates/submit/approvals.html | 2 +- ietf/templates/submit/confirm_submission.html | 2 +- 8 files changed, 44 insertions(+), 33 deletions(-) diff --git a/ietf/submit/admin.py b/ietf/submit/admin.py index e870b0659..19e7a8113 100644 --- a/ietf/submit/admin.py +++ b/ietf/submit/admin.py @@ -13,7 +13,7 @@ class SubmissionAdmin(admin.ModelAdmin): def status_link(self, instance): url = urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=instance.pk, - access_key=instance.access_key)) + access_token=instance.access_token())) return '%s' % (url, instance.state) status_link.allow_tags = True diff --git a/ietf/submit/mail.py b/ietf/submit/mail.py index bfe0f8584..42ddeff3f 100644 --- a/ietf/submit/mail.py +++ b/ietf/submit/mail.py @@ -8,6 +8,7 @@ from ietf.doc.models import Document from ietf.person.models import Person from ietf.group.models import Role from ietf.message.models import Message +from ietf.utils.accesstoken import generate_access_token def submission_confirmation_email_list(submission): try: @@ -25,8 +26,8 @@ def send_submission_confirmation(request, submission): from_email = settings.IDSUBMIT_FROM_EMAIL to_email = submission_confirmation_email_list(submission) - confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_confirm_submission', kwargs=dict(submission_id=submission.pk, auth_key=submission.auth_key)) - status_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_key=submission.access_key)) + confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_confirm_submission', kwargs=dict(submission_id=submission.pk, auth_token=generate_access_token(submission.auth_key))) + status_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_token=submission.access_token())) send_mail(request, to_email, from_email, subject, 'submit/confirm_submission.txt', { 'submission': submission, @@ -40,9 +41,8 @@ def send_full_url(request, submission): subject = 'Full URL for managing submission of draft %s' % submission.name from_email = settings.IDSUBMIT_FROM_EMAIL to_email = submission_confirmation_email_list(submission) - url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', - kwargs=dict(submission_id=submission.pk, - access_key=submission.access_key)) + url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_token=submission.access_token())) + send_mail(request, to_email, from_email, subject, 'submit/full_url.txt', { 'submission': submission, 'url': url, diff --git a/ietf/submit/models.py b/ietf/submit/models.py index d523f88fb..27184fe45 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -6,7 +6,7 @@ from django.db import models from ietf.person.models import Person from ietf.group.models import Group from ietf.name.models import DraftSubmissionStateName -from ietf.utils.uniquekey import generate_unique_key +from ietf.utils.accesstoken import generate_random_key, generate_access_token def parse_email_line(line): @@ -21,7 +21,7 @@ class Submission(models.Model): state = models.ForeignKey(DraftSubmissionStateName) remote_ip = models.CharField(max_length=100, blank=True) - access_key = models.CharField(max_length=255, default=generate_unique_key) + access_key = models.CharField(max_length=255, default=generate_random_key) auth_key = models.CharField(max_length=255, blank=True) # draft metadata @@ -59,6 +59,9 @@ class Submission(models.Model): def submitter_parsed(self): return parse_email_line(self.submitter) + def access_token(self): + return generate_access_token(self.access_key) + class SubmissionEvent(models.Model): submission = models.ForeignKey(Submission) diff --git a/ietf/submit/urls.py b/ietf/submit/urls.py index acf6091e9..049cbb0f5 100644 --- a/ietf/submit/urls.py +++ b/ietf/submit/urls.py @@ -6,9 +6,9 @@ urlpatterns = patterns('ietf.submit.views', url(r'^status/$', 'search_submission', name='submit_search_submission'), url(r'^status/(?P\d+)/$', 'submission_status', name='submit_submission_status'), url(r'^status/(?P\d+)/edit/$', 'edit_submission', name='submit_edit_submission'), - url(r'^status/(?P\d+)/confirm/(?P[a-f\d]+)/$', 'confirm_submission', name='submit_confirm_submission'), - url(r'^status/(?P\d+)/(?P[a-f\d]*)/$', 'submission_status', name='submit_submission_status_by_hash'), - url(r'^status/(?P\d+)/(?P[a-f\d]+)/edit/$', 'edit_submission', name='submit_edit_submission_by_hash'), + url(r'^status/(?P\d+)/confirm/(?P[a-f\d]+)/$', 'confirm_submission', name='submit_confirm_submission'), + url(r'^status/(?P\d+)/(?P[a-f\d]*)/$', 'submission_status', name='submit_submission_status_by_hash'), + url(r'^status/(?P\d+)/(?P[a-f\d]+)/edit/$', 'edit_submission', name='submit_edit_submission_by_hash'), url(r'^note-well/$', 'note_well', name='submit_note_well'), url(r'^tool-instructions/$', 'tool_instructions', name='submit_tool_instructions'), diff --git a/ietf/submit/views.py b/ietf/submit/views.py index edce05cd7..1878610d7 100644 --- a/ietf/submit/views.py +++ b/ietf/submit/views.py @@ -21,7 +21,7 @@ from ietf.submit.utils import approvable_submissions_for_user, preapprovals_for_ from ietf.submit.utils import check_idnits, found_idnits, validate_submission, create_submission_event from ietf.submit.utils import post_submission, cancel_submission, rename_submission_files from ietf.submit.mail import send_full_url, send_approval_request_to_group, send_submission_confirmation, submission_confirmation_email_list, send_manual_post_request -from ietf.utils.uniquekey import generate_unique_key +from ietf.utils.accesstoken import generate_random_key, generate_access_token def upload_submission(request): if request.method == 'POST': @@ -89,7 +89,7 @@ def upload_submission(request): create_submission_event(request, submission, desc="Uploaded submission") - return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_key=submission.access_key) + return redirect("submit_submission_status_by_hash", 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 = UploadForm(request=request) @@ -128,23 +128,26 @@ def search_submission(request): 'name': name}, context_instance=RequestContext(request)) -def can_edit_submission(request, submission, access_key): - key_matched = access_key and submission.access_key == access_key +def can_edit_submission(request, 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(request.user, "Secretariat") -def submission_status(request, submission_id, access_key=None, message=None): +def submission_status(request, submission_id, access_token=None): submission = get_object_or_404(Submission, pk=submission_id) - if access_key and submission.access_key != access_key: + + 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_idnits = found_idnits(submission.idnits_message) - key_matched = access_key and submission.access_key == access_key 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, submission, access_key) and submission.state_id == "uploaded" + can_edit = can_edit_submission(request, 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") @@ -161,8 +164,10 @@ def submission_status(request, submission_id, access_key=None, message=None): requires_prev_authors_approval = Document.objects.filter(name=submission.name) + message = None + if submission.state_id == "cancel": - message = ('error', 'This submission has been cancelled, modification is no longer possible.') + message = ('error', 'This submission has been canceled, 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": @@ -192,7 +197,7 @@ def submission_status(request, submission_id, access_key=None, message=None): desc = "sent approval email to group chairs: %s" % u", ".join(sent_to) else: - submission.auth_key = generate_unique_key() + submission.auth_key = generate_random_key() if requires_prev_authors_approval: submission.state = DraftSubmissionStateName.objects.get(slug="aut-appr") else: @@ -208,11 +213,11 @@ def submission_status(request, submission_id, access_key=None, message=None): create_submission_event(request, submission, u"Set submitter to \"%s\" and %s" % (submission.submitter, desc)) - return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_key=access_key) + return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_token=access_token) elif action == "edit" and submission.state_id == "uploaded": - if access_key: - return redirect("submit_edit_submission_by_hash", submission_id=submission.pk, access_key=access_key) + if access_token: + return redirect("submit_edit_submission_by_hash", submission_id=submission.pk, access_token=access_token) else: return redirect("submit_edit_submission", submission_id=submission.pk) @@ -229,7 +234,7 @@ def submission_status(request, submission_id, access_key=None, message=None): cancel_submission(submission) - create_submission_event(request, submission, "Cancelled submission") + create_submission_event(request, submission, "Canceled submission") return redirect("submit_submission_status", submission_id=submission_id) @@ -284,10 +289,10 @@ def submission_status(request, submission_id, access_key=None, message=None): context_instance=RequestContext(request)) -def edit_submission(request, submission_id, access_key=None): +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_key): + 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) @@ -360,10 +365,13 @@ def edit_submission(request, submission_id, access_key=None): context_instance=RequestContext(request)) -def confirm_submission(request, submission_id, auth_key): +def confirm_submission(request, submission_id, auth_token): submission = get_object_or_404(Submission, pk=submission_id) - if request.method == 'POST' and submission.state_id in ("auth", "aut-appr") and auth_key == submission.auth_key: + 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: post_submission(request, submission) create_submission_event(request, submission, "Confirmed and posted submission") @@ -372,7 +380,7 @@ def confirm_submission(request, submission_id, auth_key): return render_to_response('submit/confirm_submission.html', { 'submission': submission, - 'auth_key': auth_key, + 'key_matched': key_matched, }, context_instance=RequestContext(request)) diff --git a/ietf/templates/submit/approval_request.txt b/ietf/templates/submit/approval_request.txt index 46e647cd7..3eaa989fe 100644 --- a/ietf/templates/submit/approval_request.txt +++ b/ietf/templates/submit/approval_request.txt @@ -4,7 +4,7 @@ Hi, Chair approval is needed for posting of {{ submission.name }}-{{ submission.rev }}. To approve the draft, go to this URL (note: you need to login to be able to approve): - https://{{ domain }}/submit/status/{{ submission.pk }}/{{ submission.access_key }}/ + https://{{ domain }}{% url submit_submission_status_by_hash submission_id=submission.pk access_token=submission.access_token %} File name : {{ submission.name }} Revision : {{ submission.rev }} diff --git a/ietf/templates/submit/approvals.html b/ietf/templates/submit/approvals.html index cfecabd85..960eaa61b 100644 --- a/ietf/templates/submit/approvals.html +++ b/ietf/templates/submit/approvals.html @@ -28,7 +28,7 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; } {% for s in approvals %} - {{ s.name }}-{{ s.rev }} + {{ s.name }}-{{ s.rev }} {{ s.submission_date }} {% endfor %} diff --git a/ietf/templates/submit/confirm_submission.html b/ietf/templates/submit/confirm_submission.html index 8c0e4b56b..ecc10973f 100644 --- a/ietf/templates/submit/confirm_submission.html +++ b/ietf/templates/submit/confirm_submission.html @@ -23,7 +23,7 @@ p.error { color: red; font-weight: bold; font-size: 1.5em; } {% endif %} {% else %} - {% if auth_key != submission.auth_key %} + {% if not key_matched %}

      Incorrect authorization key.

      Double-check the link you followed. If everything fails, you can go to From 9f1f7790e2709438bdc50e395dce252b0f5ac518 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 16:22:29 +0000 Subject: [PATCH 111/173] Add bin/expire-submissions script for making sure old submissions are canceled automatically - Legacy-Id: 6718 --- ietf/bin/expire-submissions | 20 ++++++++++++++++ ietf/submit/tests.py | 47 +++++++++++++++++++++++++++++-------- ietf/submit/utils.py | 10 ++++++++ 3 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 ietf/bin/expire-submissions diff --git a/ietf/bin/expire-submissions b/ietf/bin/expire-submissions new file mode 100644 index 000000000..5801f721e --- /dev/null +++ b/ietf/bin/expire-submissions @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +import datetime, os +import syslog + +from ietf import settings +from django.core import management +management.setup_environ(settings) + +syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER) + +from ietf.person.models import Person +from ietf.submit.utils import expirable_submissions, expire_submission + +system = Person.objects.get(name="(System)") + +for sub in expirable_submissions(older_than_days=30): + expire_submission(sub, system) + + syslog.syslog("Expired submission %s of %s-%s" % (sub.pk, sub.name, sub.rev)) diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index 832f5680b..cde06ec1d 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -14,6 +14,8 @@ from ietf.utils.test_utils import login_testing_unauthorized from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox +from ietf.submit.utils import expirable_submissions, expire_submission + from ietf.person.models import Person, Email from ietf.group.models import Group, Role from ietf.doc.models import * @@ -523,6 +525,33 @@ class SubmitTests(django.test.TestCase): self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.ps" % (name, rev)))) self.assertTrue('This is PostScript' in open(os.path.join(self.staging_dir, u"%s-%s.ps" % (name, rev))).read()) + def test_expire_submissions(self): + s = Submission.objects.create(name="draft-ietf-mars-foo", + group=None, + submission_date=datetime.date.today() - datetime.timedelta(days=10), + rev="00", + state_id="uploaded") + + self.assertEqual(len(expirable_submissions(older_than_days=10)), 0) + self.assertEqual(len(expirable_submissions(older_than_days=9)), 1) + + s.state_id = "cancel" + s.save() + + self.assertEqual(len(expirable_submissions(older_than_days=9)), 0) + + s.state_id = "posted" + s.save() + + self.assertEqual(len(expirable_submissions(older_than_days=9)), 0) + + s.state_id = "uploaded" + s.save() + + expire_submission(s, by=None) + + self.assertEqual(s.state_id, "cancel") + class ApprovalsTests(django.test.TestCase): def test_approvals(self): @@ -535,17 +564,15 @@ class ApprovalsTests(django.test.TestCase): Preapproval.objects.create(name="draft-ietf-mars-baz", by=Person.objects.get(user__username="marschairman")) Submission.objects.create(name="draft-ietf-mars-foo", - group_id=Group.objects.get(acronym="mars").pk, - submission_date=datetime.date.today(), - rev="00", - state_id="posted", - access_key="abc") + group=Group.objects.get(acronym="mars"), + submission_date=datetime.date.today(), + rev="00", + state_id="posted") Submission.objects.create(name="draft-ietf-mars-bar", - group_id=Group.objects.get(acronym="mars").pk, - submission_date=datetime.date.today(), - rev="00", - state_id="grp-appr", - access_key="def") + group=Group.objects.get(acronym="mars"), + submission_date=datetime.date.today(), + rev="00", + state_id="grp-appr") # get r = self.client.get(url) diff --git a/ietf/submit/utils.py b/ietf/submit/utils.py index 9edd4c840..38c5e805a 100644 --- a/ietf/submit/utils.py +++ b/ietf/submit/utils.py @@ -350,3 +350,13 @@ def recently_approved_by_user(user, since): # those we can reach as chair return res.filter(group__role__name="chair", group__role__person__user=user) + +def expirable_submissions(older_than_days): + cutoff = datetime.date.today() - datetime.timedelta(days=older_than_days) + return Submission.objects.exclude(state__in=("cancel", "posted")).filter(submission_date__lt=cutoff) + +def expire_submission(submission, by): + submission.state_id = "cancel" + submission.save() + + SubmissionEvent.objects.create(submission=submission, by=by, desc="Canceled expired submission") From ce316c0adb0851c0ca9dfb6c5051917ad6e84ffd Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 16:24:38 +0000 Subject: [PATCH 112/173] assertEquals -> assertEqual in submit/tests.py (the former is apparently deprecated) - Legacy-Id: 6719 --- ietf/submit/tests.py | 200 +++++++++++++++++++++---------------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index cde06ec1d..c03078efb 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -65,25 +65,25 @@ class SubmitTests(django.test.TestCase): # get url = urlreverse('submit_upload_submission') r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('input[type=file][name=txt]')), 1) + self.assertEqual(len(q('input[type=file][name=txt]')), 1) # submit txt_file = self.submission_txt_file(name, rev) r = self.client.post(url, dict(txt=txt_file)) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) status_url = r["Location"] self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev)))) - self.assertEquals(Submission.objects.filter(name=name).count(), 1) + self.assertEqual(Submission.objects.filter(name=name).count(), 1) submission = Submission.objects.get(name=name) self.assertTrue(re.search('\s+Summary:\s+0\s+errors|No nits found', submission.idnits_message)) - self.assertEquals(len(submission.authors_parsed()), 1) + self.assertEqual(len(submission.authors_parsed()), 1) author = submission.authors_parsed()[0] - self.assertEquals(author["name"], "Author Name") - self.assertEquals(author["email"], "author@example.com") + self.assertEqual(author["name"], "Author Name") + self.assertEqual(author["email"], "author@example.com") return status_url @@ -92,7 +92,7 @@ class SubmitTests(django.test.TestCase): r = self.client.get(status_url) q = PyQuery(r.content) post_button = q('input[type=submit][value*="Post"]') - self.assertEquals(len(post_button), 1) + self.assertEqual(len(post_button), 1) action = post_button.parents("form").find('input[type=hidden][name="action"]').val() # post submitter info @@ -103,7 +103,7 @@ class SubmitTests(django.test.TestCase): }) submission = Submission.objects.get(name=name) - self.assertEquals(submission.submitter, u"%s <%s>" % (submitter_name, submitter_email)) + self.assertEqual(submission.submitter, u"%s <%s>" % (submitter_name, submitter_email)) return r @@ -132,9 +132,9 @@ class SubmitTests(django.test.TestCase): mailbox_before = len(outbox) r = self.supply_submitter(name, status_url, "Author Name", "author@example.com") - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) status_url = r["Location"] - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("New draft waiting for approval" in outbox[-1]["Subject"]) self.assertTrue(name in outbox[-1]["Subject"]) @@ -142,34 +142,34 @@ class SubmitTests(django.test.TestCase): self.client.login(remote_user="marschairman") r = self.client.get(status_url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) approve_button = q('input[type=submit][value*="Approve"]') - self.assertEquals(len(approve_button), 1) + self.assertEqual(len(approve_button), 1) action = approve_button.parents("form").find('input[type=hidden][name="action"]').val() # approve submission mailbox_before = len(outbox) r = self.client.post(status_url, dict(action=action)) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) draft = Document.objects.get(docalias__name=name) - self.assertEquals(draft.rev, rev) + self.assertEqual(draft.rev, rev) new_revision = draft.latest_event() - self.assertEquals(draft.group.acronym, "mars") - self.assertEquals(new_revision.type, "new_revision") - self.assertEquals(new_revision.by.name, "Author Name") + self.assertEqual(draft.group.acronym, "mars") + self.assertEqual(new_revision.type, "new_revision") + self.assertEqual(new_revision.by.name, "Author Name") self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev)))) self.assertTrue(os.path.exists(os.path.join(self.repository_dir, u"%s-%s.txt" % (name, rev)))) - self.assertEquals(draft.type_id, "draft") - self.assertEquals(draft.stream_id, "ietf") + self.assertEqual(draft.type_id, "draft") + self.assertEqual(draft.stream_id, "ietf") self.assertTrue(draft.expires >= datetime.datetime.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE - 1)) - self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id).slug, "wg-doc") - self.assertEquals(draft.authors.count(), 1) - self.assertEquals(draft.authors.all()[0].get_name(), "Author Name") - self.assertEquals(draft.authors.all()[0].address, "author@example.com") - self.assertEquals(len(outbox), mailbox_before + 2) + self.assertEqual(draft.get_state("draft-stream-%s" % draft.stream_id).slug, "wg-doc") + self.assertEqual(draft.authors.count(), 1) + self.assertEqual(draft.authors.all()[0].get_name(), "Author Name") + self.assertEqual(draft.authors.all()[0].address, "author@example.com") + self.assertEqual(len(outbox), mailbox_before + 2) self.assertTrue((u"I-D Action: %s" % name) in outbox[-2]["Subject"]) self.assertTrue("Author Name" in unicode(outbox[-2])) self.assertTrue("New Version Notification" in outbox[-1]["Subject"]) @@ -213,13 +213,13 @@ class SubmitTests(django.test.TestCase): # supply submitter info, then previous authors get a confirmation email mailbox_before = len(outbox) r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com") - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) status_url = r["Location"] r = self.client.get(status_url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) self.assertTrue("The submission is pending approval by the authors" in r.content) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) confirm_email = outbox[-1] self.assertTrue("Confirm submission" in confirm_email["Subject"]) self.assertTrue(name in confirm_email["Subject"]) @@ -233,30 +233,30 @@ class SubmitTests(django.test.TestCase): # go to confirm page r = self.client.get(confirm_url) q = PyQuery(r.content) - self.assertEquals(len(q('input[type=submit][value*="Confirm"]')), 1) + self.assertEqual(len(q('input[type=submit][value*="Confirm"]')), 1) # confirm mailbox_before = len(outbox) r = self.client.post(confirm_url) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) draft = Document.objects.get(docalias__name=name) - self.assertEquals(draft.rev, rev) - self.assertEquals(draft.group.acronym, name.split("-")[2]) - self.assertEquals(draft.docevent_set.all()[1].type, "new_revision") - self.assertEquals(draft.docevent_set.all()[1].by.name, "Submitter Name") + self.assertEqual(draft.rev, rev) + self.assertEqual(draft.group.acronym, name.split("-")[2]) + self.assertEqual(draft.docevent_set.all()[1].type, "new_revision") + self.assertEqual(draft.docevent_set.all()[1].by.name, "Submitter Name") self.assertTrue(not os.path.exists(os.path.join(self.repository_dir, "%s-%s.txt" % (name, old_rev)))) self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "%s-%s.txt" % (name, old_rev)))) self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev)))) self.assertTrue(os.path.exists(os.path.join(self.repository_dir, u"%s-%s.txt" % (name, rev)))) - self.assertEquals(draft.type_id, "draft") - self.assertEquals(draft.stream_id, "ietf") - self.assertEquals(draft.get_state_slug("draft-stream-%s" % draft.stream_id), "wg-doc") - self.assertEquals(draft.get_state_slug("draft-iana-review"), "changed") - self.assertEquals(draft.authors.count(), 1) - self.assertEquals(draft.authors.all()[0].get_name(), "Author Name") - self.assertEquals(draft.authors.all()[0].address, "author@example.com") - self.assertEquals(len(outbox), mailbox_before + 3) + self.assertEqual(draft.type_id, "draft") + self.assertEqual(draft.stream_id, "ietf") + self.assertEqual(draft.get_state_slug("draft-stream-%s" % draft.stream_id), "wg-doc") + self.assertEqual(draft.get_state_slug("draft-iana-review"), "changed") + self.assertEqual(draft.authors.count(), 1) + self.assertEqual(draft.authors.all()[0].get_name(), "Author Name") + self.assertEqual(draft.authors.all()[0].address, "author@example.com") + self.assertEqual(len(outbox), mailbox_before + 3) self.assertTrue((u"I-D Action: %s" % name) in outbox[-3]["Subject"]) self.assertTrue((u"I-D Action: %s" % name) in draft.message_set.order_by("-time")[0].subject) self.assertTrue("Author Name" in unicode(outbox[-3])) @@ -282,13 +282,13 @@ class SubmitTests(django.test.TestCase): mailbox_before = len(outbox) r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com") - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) status_url = r["Location"] r = self.client.get(status_url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) self.assertTrue("The submission is pending email authentication" in r.content) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) confirm_email = outbox[-1] self.assertTrue("Confirm submission" in confirm_email["Subject"]) self.assertTrue(name in confirm_email["Subject"]) @@ -301,18 +301,18 @@ class SubmitTests(django.test.TestCase): # go to confirm page r = self.client.get(confirm_url) q = PyQuery(r.content) - self.assertEquals(len(q('input[type=submit][value*="Confirm"]')), 1) + self.assertEqual(len(q('input[type=submit][value*="Confirm"]')), 1) # confirm mailbox_before = len(outbox) r = self.client.post(confirm_url) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) draft = Document.objects.get(docalias__name=name) - self.assertEquals(draft.rev, rev) + self.assertEqual(draft.rev, rev) new_revision = draft.latest_event() - self.assertEquals(new_revision.type, "new_revision") - self.assertEquals(new_revision.by.name, "Submitter Name") + self.assertEqual(new_revision.type, "new_revision") + self.assertEqual(new_revision.by.name, "Submitter Name") def test_submit_new_wg_with_dash(self): draft = make_test_data() @@ -323,7 +323,7 @@ class SubmitTests(django.test.TestCase): self.do_submission(name, "00") - self.assertEquals(Submission.objects.get(name=name).group.acronym, group.acronym) + self.assertEqual(Submission.objects.get(name=name).group.acronym, group.acronym) def test_submit_new_irtf(self): draft = make_test_data() @@ -334,8 +334,8 @@ class SubmitTests(django.test.TestCase): self.do_submission(name, "00") - self.assertEquals(Submission.objects.get(name=name).group.acronym, group.acronym) - self.assertEquals(Submission.objects.get(name=name).group.type_id, group.type_id) + self.assertEqual(Submission.objects.get(name=name).group.acronym, group.acronym) + self.assertEqual(Submission.objects.get(name=name).group.type_id, group.type_id) def test_submit_new_iab(self): draft = make_test_data() @@ -344,7 +344,7 @@ class SubmitTests(django.test.TestCase): self.do_submission(name, "00") - self.assertEquals(Submission.objects.get(name=name).group.acronym, "iab") + self.assertEqual(Submission.objects.get(name=name).group.acronym, "iab") def test_cancel_submission(self): # submit -> cancel @@ -357,10 +357,10 @@ class SubmitTests(django.test.TestCase): # check we got cancel button r = self.client.get(status_url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) cancel_button = q('input[type=submit][value*="Cancel"]') - self.assertEquals(len(cancel_button), 1) + self.assertEqual(len(cancel_button), 1) action = cancel_button.parents("form").find("input[type=hidden][name=\"action\"]").val() @@ -379,23 +379,23 @@ class SubmitTests(django.test.TestCase): # check we got edit button r = self.client.get(status_url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) adjust_button = q('input[type=submit][value*="Adjust"]') - self.assertEquals(len(adjust_button), 1) + self.assertEqual(len(adjust_button), 1) action = adjust_button.parents("form").find('input[type=hidden][name="action"]').val() # go to edit, we do this by posting, slightly weird r = self.client.post(status_url, dict(action=action)) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) edit_url = r['Location'] # check page r = self.client.get(edit_url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('input[name=edit-title]')), 1) + self.assertEqual(len(q('input[name=edit-title]')), 1) # edit mailbox_before = len(outbox) @@ -415,25 +415,25 @@ class SubmitTests(django.test.TestCase): "authors-1-email": "person2@example.com", "authors-prefix": ["authors-", "authors-0", "authors-1"], }) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) submission = Submission.objects.get(name=name) - self.assertEquals(submission.title, "some title") - self.assertEquals(submission.document_date, document_date) - self.assertEquals(submission.abstract, "some abstract") - self.assertEquals(submission.pages, 123) - self.assertEquals(submission.note, "no comments") - self.assertEquals(submission.submitter, "Some Random Test Person ") - self.assertEquals(submission.state_id, "manual") + self.assertEqual(submission.title, "some title") + self.assertEqual(submission.document_date, document_date) + self.assertEqual(submission.abstract, "some abstract") + self.assertEqual(submission.pages, 123) + self.assertEqual(submission.note, "no comments") + self.assertEqual(submission.submitter, "Some Random Test Person ") + self.assertEqual(submission.state_id, "manual") authors = submission.authors_parsed() - self.assertEquals(len(authors), 2) - self.assertEquals(authors[0]["name"], "Person 1") - self.assertEquals(authors[0]["email"], "person1@example.com") - self.assertEquals(authors[1]["name"], "Person 2") - self.assertEquals(authors[1]["email"], "person2@example.com") + self.assertEqual(len(authors), 2) + self.assertEqual(authors[0]["name"], "Person 1") + self.assertEqual(authors[0]["email"], "person1@example.com") + self.assertEqual(authors[1]["name"], "Person 2") + self.assertEqual(authors[1]["email"], "person2@example.com") - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("Manual Post Requested" in outbox[-1]["Subject"]) self.assertTrue(name in outbox[-1]["Subject"]) @@ -441,20 +441,20 @@ class SubmitTests(django.test.TestCase): self.client.login(remote_user="secretary") r = self.client.get(status_url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) post_button = q('input[type=submit][value*="Force"]') - self.assertEquals(len(post_button), 1) + self.assertEqual(len(post_button), 1) action = post_button.parents("form").find('input[type=hidden][name="action"]').val() # force post mailbox_before = len(outbox) r = self.client.post(status_url, dict(action=action)) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) draft = Document.objects.get(docalias__name=name) - self.assertEquals(draft.rev, rev) + self.assertEqual(draft.rev, rev) def test_request_full_url(self): # submit -> request full URL to be sent @@ -470,19 +470,19 @@ class SubmitTests(django.test.TestCase): # check we got request full URL button r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) request_button = q('input[type=submit][value*="Request full access"]') - self.assertEquals(len(request_button), 1) + self.assertEqual(len(request_button), 1) # request URL to be sent mailbox_before = len(outbox) action = request_button.parents("form").find("input[type=hidden][name=\"action\"]").val() r = self.client.post(url, dict(action=action)) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("Full URL for managing submission" in outbox[-1]["Subject"]) self.assertTrue(name in outbox[-1]["Subject"]) @@ -512,9 +512,9 @@ class SubmitTests(django.test.TestCase): pdf=pdf_file, ps=ps_file, )) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) - self.assertEquals(Submission.objects.filter(name=name).count(), 1) + self.assertEqual(Submission.objects.filter(name=name).count(), 1) self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev)))) self.assertTrue(name in open(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))).read()) @@ -576,14 +576,14 @@ class ApprovalsTests(django.test.TestCase): # get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('.approvals a:contains("draft-ietf-mars-foo")')), 0) - self.assertEquals(len(q('.approvals a:contains("draft-ietf-mars-bar")')), 1) - self.assertEquals(len(q('.preapprovals td:contains("draft-ietf-mars-foo")')), 0) - self.assertEquals(len(q('.preapprovals td:contains("draft-ietf-mars-baz")')), 1) - self.assertEquals(len(q('.recently-approved a:contains("draft-ietf-mars-foo")')), 1) + self.assertEqual(len(q('.approvals a:contains("draft-ietf-mars-foo")')), 0) + self.assertEqual(len(q('.approvals a:contains("draft-ietf-mars-bar")')), 1) + self.assertEqual(len(q('.preapprovals td:contains("draft-ietf-mars-foo")')), 0) + self.assertEqual(len(q('.preapprovals td:contains("draft-ietf-mars-baz")')), 1) + self.assertEqual(len(q('.recently-approved a:contains("draft-ietf-mars-foo")')), 1) def test_add_preapproval(self): make_test_data() @@ -593,21 +593,21 @@ class ApprovalsTests(django.test.TestCase): # get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('input[type=submit]')), 1) + self.assertEqual(len(q('input[type=submit]')), 1) # faulty post r = self.client.post(url, dict(name="draft-test-nonexistingwg-something")) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) self.assertTrue("errorlist" in r.content) # add name = "draft-ietf-mars-foo" r = self.client.post(url, dict(name=name)) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) - self.assertEquals(len(Preapproval.objects.filter(name=name)), 1) + self.assertEqual(len(Preapproval.objects.filter(name=name)), 1) def test_cancel_preapproval(self): make_test_data() @@ -619,12 +619,12 @@ class ApprovalsTests(django.test.TestCase): # get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('input[type=submit]')), 1) + self.assertEqual(len(q('input[type=submit]')), 1) # cancel r = self.client.post(url, dict(action="cancel")) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) - self.assertEquals(len(Preapproval.objects.filter(name=preapproval.name)), 0) + self.assertEqual(len(Preapproval.objects.filter(name=preapproval.name)), 0) From 66bff20847745a9472e60e6c46d3ca96e4d1ad21 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 19 Nov 2013 16:07:29 +0000 Subject: [PATCH 113/173] Remove legacy ballot_json view which has not worked for a while - Legacy-Id: 6720 --- ietf/doc/urls.py | 1 - ietf/doc/views_doc.py | 42 ------------------------------------------ 2 files changed, 43 deletions(-) diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index 6d46b3017..0c9e569ae 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -66,7 +66,6 @@ urlpatterns += patterns('', url(r'^(?P[A-Za-z0-9._+-]+)/ballot/$', views_doc.document_ballot, name="doc_ballot"), (r'^(?P[A-Za-z0-9._+-]+)/doc.json$', views_doc.document_json), (r'^(?P[A-Za-z0-9._+-]+)/ballotpopup/(?P[0-9]+)/$', views_doc.ballot_popup), - #(r'^(?P[A-Za-z0-9._+-]+)/ballot.json$', views_doc.ballot_json), # legacy view url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/$', views_draft.change_state, name='doc_change_state'), # IESG state url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/(?Piana-action|iana-review)/$', views_draft.change_iana_state, name='doc_change_iana_state'), diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 18f540591..973b96ffd 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -732,48 +732,6 @@ def document_json(request, name): return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain') -def ballot_json(request, name): - # REDESIGN: this view needs to be deleted or updated - def get_ballot(name): - from ietf.doc.models import DocAlias - alias = get_object_or_404(DocAlias, name=name) - d = alias.document - from ietf.idtracker.models import InternetDraft, BallotInfo - from ietf.idrfc.idrfc_wrapper import BallotWrapper, IdWrapper, RfcWrapper - id = None - bw = None - dw = None - if (d.type_id=='draft'): - id = get_object_or_404(InternetDraft, name=d.name) - try: - if not id.ballot.ballot_issued: - raise Http404 - except BallotInfo.DoesNotExist: - raise Http404 - - bw = BallotWrapper(id) # XXX Fixme: Eliminate this as we go forward - # Python caches ~100 regex'es -- explicitly compiling it inside a method - # (where you then throw away the compiled version!) doesn't make sense at - # all. - if re.search("^rfc([1-9][0-9]*)$", name): - id.viewing_as_rfc = True - dw = RfcWrapper(id) - else: - dw = IdWrapper(id) - # XXX Fixme: Eliminate 'dw' as we go forward - - try: - b = d.latest_event(BallotDocEvent, type="created_ballot") - except BallotDocEvent.DoesNotExist: - raise Http404 - - return (bw, dw, b, d) - - ballot, doc, b, d = get_ballot(name) - response = HttpResponse(mimetype='text/plain') - response.write(json.dumps(ballot.dict(), indent=2)) - return response - class AddCommentForm(forms.Form): comment = forms.CharField(required=True, widget=forms.Textarea) From 10a4d286cd298abfd2cbc547c4df456857c89810 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 19 Nov 2013 16:10:29 +0000 Subject: [PATCH 114/173] Remove now unused idrfc_wrapper.py, remove idrfc app - Legacy-Id: 6721 --- ietf/idrfc/.gitignore | 1 - ietf/idrfc/__init__.py | 1 - ietf/idrfc/idrfc_wrapper.py | 967 ---------------------------- ietf/idrfc/mirror_draft_versions.py | 88 --- ietf/idrfc/models.py | 104 --- ietf/settings.py | 1 - 6 files changed, 1162 deletions(-) delete mode 100644 ietf/idrfc/.gitignore delete mode 100644 ietf/idrfc/__init__.py delete mode 100644 ietf/idrfc/idrfc_wrapper.py delete mode 100644 ietf/idrfc/mirror_draft_versions.py delete mode 100644 ietf/idrfc/models.py diff --git a/ietf/idrfc/.gitignore b/ietf/idrfc/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/idrfc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/idrfc/__init__.py b/ietf/idrfc/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/ietf/idrfc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/ietf/idrfc/idrfc_wrapper.py b/ietf/idrfc/idrfc_wrapper.py deleted file mode 100644 index feb57d199..000000000 --- a/ietf/idrfc/idrfc_wrapper.py +++ /dev/null @@ -1,967 +0,0 @@ -# Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). -# All rights reserved. Contact: Pasi Eronen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the Nokia Corporation and/or its -# subsidiary(-ies) nor the names of its contributors may be used -# to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, IESGDiscuss, IESGLogin, DocumentComment, Acronym, IDState -from ietf.idrfc.models import RfcEditorQueue -from ietf.ipr.models import IprRfc, IprDraft, IprDetail -from ietf.doc.models import BallotDocEvent - -import re -from datetime import date -from django.utils import simplejson as json -from django.db.models import Q -from django.db import models -from django.core.urlresolvers import reverse -from django.conf import settings -import types -import debug - -BALLOT_ACTIVE_STATES = ['In Last Call', - 'Waiting for Writeup', - 'Waiting for AD Go-Ahead', - 'IESG Evaluation', - 'IESG Evaluation - Defer'] - -def jsonify_helper(obj, keys): - result = {} - for k in keys: - if hasattr(obj, k): - v = getattr(obj, k) - if callable(v): - v = v() - if v == None: - pass - elif isinstance(v, (types.StringType, types.IntType, types.BooleanType, types.LongType, types.ListType, types.UnicodeType)): - result[k] = v - elif isinstance(v, date): - result[k] = str(v) - else: - result[k] = 'Unknown type '+str(type(v)) - return result - -# Wrappers to make writing templates less painful - -# --------------------------------------------------------------------------- - -class IdWrapper: - _draft = None - _idinternal = None - - is_id_wrapper = True - is_rfc_wrapper = False - - draft_name = None - # Active/Expired/RFC/Withdrawn by Submitter/Replaced/Withdrawn by IETF - draft_status = None - # Revision is sometimes incorrect (+1 too large) if status != Active - latest_revision = None - # Set if and only if draft_status is "RFC" - rfc_number = None - title = None - tracker_id = None - publication_date = None - ietf_process = None - - def __init__(self, draft): - self.id = self - if isinstance(draft, IDInternal) and not settings.USE_DB_REDESIGN_PROXY_CLASSES: - self._idinternal = draft - self._draft = self._idinternal.draft - else: - self._draft = draft - if draft.idinternal: - self._idinternal = draft.idinternal - if self._idinternal: - self.ietf_process = IetfProcessData(self._idinternal) - - self.draft_name = self._draft.filename - self.draft_status = str(self._draft.status) - if self.draft_status == "RFC": - if self._draft.rfc_number: - self.rfc_number = self._draft.rfc_number - else: - # Handle incorrect database entries - self.draft_status = "Expired" - self.latest_revision = self._draft.revision_display() - self.title = self._draft.title - self.tracker_id = self._draft.id_document_tag - self.resurrect_requested_by = self._idinternal.resurrect_requested_by if self._idinternal else None - self.publication_date = self._draft.revision_date - if not self.publication_date: - # should never happen -- but unfortunately it does. Return an - # obviously bogus date - self.publication_date = date(1990,1,1) - - def rfc_editor_state(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - s = self._draft.get_state("draft-rfceditor") - if s: - # extract possible extra annotations - tags = self._draft.tags.filter(slug__in=("iana", "ref")) - return "*".join([s.name] + [t.slug.upper() for t in tags]) - else: - return None - - try: - qs = self._draft.rfc_editor_queue_state - return qs.state - except RfcEditorQueue.DoesNotExist: - pass - return None - - def replaced_by(self): - try: - if self._draft.replaced_by: - return [self._draft.replaced_by.filename] - except InternetDraft.DoesNotExist: - pass - return None - def replaces(self): - r = [str(r.filename) for r in self._draft.replaces_set.all()] - if len(r) > 0: - return r - else: - return None - def in_ietf_process(self): - return self.ietf_process != None - - def submission(self): - - if self._draft.stream_id != u'ietf': - return self._draft.stream - - if self._draft.group_id == Acronym.INDIVIDUAL_SUBMITTER: - return "Individual" - - if self._draft.group and self._draft.group.type_id == "area": - return u"Individual in %s area" % self._draft.group.acronym - - a = self.group_acronym() - if a: - if self._draft.stream_id == "ietf" and self._draft.get_state_slug("draft-stream-ietf") == "c-adopt": - return "candidate for %s WG" % (a, a) - - return "%s WG" % (a, a) - - return "" - submission.allow_tags = True - - def search_archive(self): - - if self._idinternal and self._idinternal.stream in ("IRTF","ISE"): - return "www.ietf.org/mail-archive/web/" - - if self._draft.group_id == Acronym.INDIVIDUAL_SUBMITTER or (settings.USE_DB_REDESIGN_PROXY_CLASSES and self._draft.group.type_id == "area"): - return "www.ietf.org/mail-archive/web/" - - a = self._draft.group_ml_archive() - if a: - return a - - return "" - - def file_types(self): - return self._draft.file_type.split(",") - - def group_acronym(self): - if self._draft.group_id != 0 and self._draft.group != None and str(self._draft.group) != "none": - if settings.USE_DB_REDESIGN_PROXY_CLASSES and self._draft.group.type_id == "area": - return None - return str(self._draft.group) - else: - return None - - # TODO: Returning integers here isn't nice - # 0=Unknown, 1=IETF, 2=IAB, 3=IRTF, 4=Independent - def stream_id(self): - if self.draft_name.startswith("draft-iab-"): - return 2 - elif self.draft_name.startswith("draft-irtf-"): - return 3 - elif self._idinternal: - if self._idinternal.stream == "ISE": - return 4 - else: - return 1 - elif self.group_acronym(): - return 1 - else: - return 0 - - def draft_name_and_revision(self): - return self.draft_name+"-"+self.latest_revision - - def friendly_state(self): - if self.draft_status == "RFC": - return "RFC %d" % (reverse('doc_view', args=['rfc%d' % self.rfc_number]), self.rfc_number) - elif self.draft_status == "Active": - if self.in_ietf_process(): - if self.ietf_process.main_state == "Dead": - # Many drafts in "Dead" state are not dead; they're - # just not currently under IESG processing. Show - # them as "I-D Exists (IESG: Dead)" instead... - return "I-D Exists (IESG: "+self.ietf_process.state+")" - elif self.ietf_process.main_state == "In Last Call": - return self.ietf_process.state + " (ends "+str(self._idinternal.document().lc_expiration_date)+")" - else: - return self.ietf_process.state - else: - return "I-D Exists" - else: - if self.in_ietf_process() and self.ietf_process.main_state == "Dead": - return self.draft_status+" (IESG: "+self.ietf_process.state+")" - # Expired/Withdrawn by Submitter/IETF - return self.draft_status - - def abstract(self): - return self._draft.clean_abstract() - - # TODO: ugly hack - def authors(self): - return self._draft.authors - - def expected_expiration_date(self): - if self.draft_status == "Active" and self._draft.can_expire(): - return self._draft.expiration() - else: - return None - - def ad_name(self): - if self.in_ietf_process(): - return self.ietf_process.ad_name() - else: - return None - - def get_absolute_url(self): - return "/doc/"+self.draft_name+"/" - def displayname_with_link(self): - return '%s' % (self.get_absolute_url(), self.draft_name_and_revision()) - - def underlying_document(self): - """ Expose the Document object underneath the proxy """ - from ietf.doc.models import Document - return Document.objects.get(docalias__name=self.draft_name) - - def to_json(self): - result = jsonify_helper(self, ['draft_name', 'draft_status', 'latest_revision', 'rfc_number', 'title', 'tracker_id', 'publication_date','rfc_editor_state', 'replaced_by', 'replaces', 'in_ietf_process', 'file_types', 'group_acronym', 'stream_id','friendly_state', 'abstract', 'ad_name']) - if self.in_ietf_process(): - result['ietf_process'] = self.ietf_process.dict() - return json.dumps(result, indent=2) - -# --------------------------------------------------------------------------- - -class RfcWrapper: - _rfc = None - _rfcindex = None - _idinternal = None - - is_id_wrapper = False - is_rfc_wrapper = True - - rfc_number = None - title = None - publication_date = None - maturity_level = None - ietf_process = None - draft_name = None - - def __init__(self, rfcindex, rfc=None, idinternal=None): - self._rfcindex = rfcindex - self._rfc = rfc - self._idinternal = idinternal - self.rfc = self - - if not self._idinternal: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - pub = rfcindex.rfc_published_date - started = rfcindex.started_iesg_process if hasattr(rfcindex, 'started_iesg_process') else rfcindex.latest_event(type="started_iesg_process") - if pub and started and pub < started.time.date(): - self._idinternal = rfcindex - else: - try: - self._idinternal = IDInternal.objects.get(rfc_flag=1, draft=self._rfcindex.rfc_number) - except IDInternal.DoesNotExist: - pass - - if self._idinternal: - self.ietf_process = IetfProcessData(self._idinternal) - - self.rfc_number = self._rfcindex.rfc_number - self.title = self._rfcindex.title - self.publication_date = self._rfcindex.rfc_published_date - self.maturity_level = self._rfcindex.current_status - if not self.maturity_level: - self.maturity_level = "Unknown" - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if not rfcindex.name.startswith('rfc'): - self.draft_name = rfcindex.name - return # we've already done the lookup while importing so skip the rest - - ids = InternetDraft.objects.filter(rfc_number=self.rfc_number) - if len(ids) >= 1: - self.draft_name = ids[0].filename - elif self._rfcindex and self._rfcindex.draft: - # rfcindex occasionally includes drafts that were not - # really submitted to IETF (e.g. April 1st) - ids = InternetDraft.objects.filter(filename=self._rfcindex.draft) - if len(ids) > 0: - self.draft_name = self._rfcindex.draft - - def _rfc_doc_list(self, name): - if (not self._rfcindex) or (not getattr(self._rfcindex, name)): - return None - else: - s = getattr(self._rfcindex, name) - s = s.replace(",", ", ") - s = re.sub("([A-Z])([0-9])", "\\1 \\2", s) - return s - def obsoleted_by(self): - return self._rfc_doc_list("obsoleted_by") - def obsoletes(self): - return self._rfc_doc_list("obsoletes") - def updated_by(self): - return self._rfc_doc_list("updated_by") - def updates(self): - return self._rfc_doc_list("updates") - def also(self): - return self._rfc_doc_list("also") - def has_errata(self): - return self._rfcindex and (self._rfcindex.has_errata > 0) - def stream_name(self): - if not self._rfcindex: - return None - else: - x = self._rfcindex.stream - if x == "INDEPENDENT": - return "Independent Submission Stream" - elif x == "LEGACY": - return "Legacy Stream" - else: - return x+" Stream" - - def in_ietf_process(self): - return self.ietf_process != None - - def file_types(self): - types = self._rfcindex.file_formats - types = types.replace("ascii","txt") - return ["."+x for x in types.split(",")] - - def friendly_state(self): - if self.in_ietf_process(): - s = self.ietf_process.main_state - if not s in ["RFC Published", "AD is watching", "Dead"]: - return "RFC %d (%s)
      %s (to %s)" % (self.rfc_number, self.maturity_level, self.ietf_process.state, self.ietf_process.intended_maturity_level()) - return "RFC %d (%s)" % (self.rfc_number, self.maturity_level) - - def ad_name(self): - if self.in_ietf_process(): - return self.ietf_process.ad_name() - else: - # TODO: get AD name of the draft - return None - def filename(self): - return self._rfcindex.filename - - @models.permalink - def get_absolute_url(self): - return ('ietf.doc.views_doc.document_main', ['rfc%s' % (str(self.rfc_number))]) - def displayname_with_link(self): - return 'RFC %d' % (self.get_absolute_url(), self.rfc_number) - - def to_json(self): - result = jsonify_helper(self, ['rfc_number', 'title', 'publication_date', 'maturity_level', 'obsoleted_by','obsoletes','updated_by','updates','also','has_errata','stream_name','file_types','in_ietf_process', 'friendly_state']) - if self.in_ietf_process(): - result['ietf_process'] = self.ietf_process.dict() - return json.dumps(result, indent=2) - - def underlying_document(self): - """ Expose the Document object underneath the proxy """ - # Things like RFC500 are special - there may not _be_ a docalias for them - from ietf.doc.models import Document - q = Document.objects.filter(docalias__name='rfc%04d'%self.rfc_number) - if q: - return q[0] - else: - return None - -# --------------------------------------------------------------------------- - -class IetfProcessData: - _idinternal = None - main_state = None - sub_state = None - state = None - _ballot = None - def __init__(self, idinternal): - self._idinternal = idinternal - i = self._idinternal - self.main_state = str(i.cur_state) - if i.cur_sub_state_id > 0: - self.sub_state = str(i.cur_sub_state) - self.state = self.main_state + "::" + self.sub_state - else: - self.sub_state = None - self.state = self.main_state - - def has_iesg_ballot(self): - try: - if self._idinternal.ballot.ballot_issued: - return True - except BallotInfo.DoesNotExist: - pass - return False - - def has_active_iesg_ballot(self): - if not self.has_iesg_ballot(): - return False - if not self.main_state in BALLOT_ACTIVE_STATES: - return False - if (not self._idinternal.rfc_flag) and self._idinternal.draft.status_id != 1: - # Active - return False - return True - - # don't call this unless has_[active_]iesg_ballot returns True - def iesg_ballot(self): - if not self._ballot: - self._ballot = BallotWrapper(self._idinternal) - return self._ballot - - # don't call this unless has_[active_]iesg_ballot returns True - def iesg_ballot_needed( self ): - standardsTrack = 'Standard' in self.intended_maturity_level() or \ - self.intended_maturity_level() in ("BCP", "Best Current Practice") - return self.iesg_ballot().ballot.needed( standardsTrack ) - - def ad_name(self): - return str(self._idinternal.job_owner) - - def iesg_note(self): - if self._idinternal.note: - n = self._idinternal.note - # Hide unnecessary note of form "RFC 1234" - if re.match("^RFC\s*\d+$", n): - return None - return n - else: - return None - - def state_date(self): - try: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return self._idinternal.docevent_set.filter( - Q(desc__istartswith="Draft Added by ")| - Q(desc__istartswith="Draft Added in state ")| - Q(desc__istartswith="Draft added in state ")| - Q(desc__istartswith="State changed to ")| - Q(desc__istartswith="State Changes to ")| - Q(desc__istartswith="Sub state has been changed to ")| - Q(desc__istartswith="State has been changed to ")| - Q(desc__istartswith="IESG has approved and state has been changed to")| - Q(desc__istartswith="IESG process started in state")).order_by('-time')[0].time.date() - return self._idinternal.comments().filter( - Q(comment_text__istartswith="Draft Added by ")| - Q(comment_text__istartswith="Draft Added in state ")| - Q(comment_text__istartswith="Draft added in state ")| - Q(comment_text__istartswith="State changed to ")| - Q(comment_text__istartswith="State Changes to ")| - Q(comment_text__istartswith="Sub state has been changed to ")| - Q(comment_text__istartswith="State has been changed to ")| - Q(comment_text__istartswith="IESG has approved and state has been changed to")).order_by('-id')[0].date - except IndexError: - # should never happen -- return an obviously bogus date - return date(1990,1,1) - - def dict(self): - result = {'main_state':self.main_state, - 'sub_state':self.sub_state, - 'state':self.state, - 'state_date':str(self.state_date()), - 'has_iesg_ballot':self.has_iesg_ballot(), - 'has_active_iesg_ballot':self.has_active_iesg_ballot(), - 'ad_name':self.ad_name(), - 'intended_maturity_level':self.intended_maturity_level(), - 'telechat_date':self.telechat_date()} - if result['telechat_date']: - result['telechat_date'] = str(result['telechat_date']) - result['telechat_returning_item'] = self.telechat_returning_item() - if self.iesg_note(): - result['iesg_note'] = self.iesg_note() - if self.has_iesg_ballot(): - result['iesg_ballot'] = self.iesg_ballot().dict() - return result - - def intended_maturity_level(self): - if self._idinternal.rfc_flag: - s = str(self._idinternal.document().intended_status) - # rfc_intend_status table uses different names, argh! - if s == "Proposed": - s = "Proposed Standard" - elif s == "Draft": - s = "Draft Standard" - elif s == "None": - s = None - else: - s = str(self._idinternal.draft.intended_status) - if s == "None": - s = None - elif s == "Request": - s = None - return s - - def telechat_date(self): - # return date only if it's on upcoming agenda - if self._idinternal.agenda: - return self._idinternal.telechat_date - else: - return None - - def telechat_returning_item(self): - # should be called only if telechat_date() returns non-None - return bool(self._idinternal.returning_item) - - def state_change_notice_to(self): - return self._idinternal.state_change_notice_to - - # comment_log? - -# --------------------------------------------------------------------------- - -class IdRfcWrapper: - rfc = None - id = None - iprCount = None - iprUrl = None - - def __init__(self, id, rfc): - self.id = id - self.rfc = rfc - if id: - iprs = IprDraft.objects.filter(document=self.id.tracker_id, ipr__status__in=[1,3]) - self.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(self.id.tracker_id) - elif rfc: - iprs = IprRfc.objects.filter(document=self.rfc.rfc_number, ipr__status__in=[1,3]) - self.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(self.rfc.rfc_number) - else: - raise ValueError("Construction with null id and rfc") - # iprs is a list of docs which contain IPR - self.iprCount = len(iprs) - - def title(self): - if self.rfc: - return self.rfc.title - else: - return self.id.title - - def friendly_state(self): - if self.rfc: - return self.rfc.friendly_state() - else: - return self.id.friendly_state() - - def get_absolute_url(self): - if self.rfc: - return self.rfc.get_absolute_url() - else: - return self.id.get_absolute_url() - - def comment_count(self): - if self.rfc: - return DocumentComment.objects.filter(document=self.rfc.rfc_number,rfc_flag=1).count() - else: - return DocumentComment.objects.filter(document=self.id.tracker_id).exclude(rfc_flag=1).count() - - def ad_name(self): - if self.rfc: - s = self.rfc.ad_name() - if s: - return s - if self.id: - return self.id.ad_name() - return None - - def publication_date(self): - if self.rfc: - return self.rfc.publication_date - else: - return self.id.publication_date - - def telechat_date(self): - if self.rfc and self.rfc.in_ietf_process(): - return self.rfc.ietf_process.telechat_date() - elif self.id and self.id.in_ietf_process(): - return self.id.ietf_process.telechat_date() - else: - return None - - def view_sort_group(self): - if self.rfc: - return 'RFC' - elif self.id.draft_status == "Active": - return 'Active Internet-Draft' - else: - return 'Old Internet-Draft' - - def view_sort_group_byad(self): - if self.rfc: - return 'RFC' - elif self.id.draft_status == "Active": - if self.id.in_ietf_process(): - if self.id.ietf_process._idinternal.cur_state_id == IDState.DEAD: - return 'IESG Dead Internet-Draft' - else: - return "%s Internet-Draft" % self.id.ietf_process._idinternal.cur_state - else: - return 'Active Internet-Draft' - else: - return 'Old Internet-Draft' - - def view_sort_key(self, sort_by=None): - if sort_by is None: - if self.rfc: - return "2%04d" % self.rfc.rfc_number - elif self.id.draft_status == "Active": - return "1"+self.id.draft_name - else: - return "3"+self.id.draft_name - else: - if self.rfc: - sort_key = "2" - elif self.id.draft_status == "Active": - sort_key = "1" - else: - sort_key = "3" - - # Depending on what we're sorting on, we may - # need to do some conversion. - if sort_by == "title": - sort_key += self.title() - elif sort_by == "date": - sort_key = sort_key + str(self.publication_date()) - elif sort_by == "status": - if self.rfc: - sort_key += "%04d" % self.rfc.rfc_number - else: - sort_key += self.id.draft_status - elif sort_by == "ipr": - sort_key += self.iprUrl - elif sort_by == "ad": - return self.view_sort_key_byad() - else: - # sort default or unknown sort value, revert to default - if self.rfc: - sort_key += "%04d" % self.rfc.rfc_number - else: - sort_key += self.id.draft_name - - return sort_key - - def view_sort_key_byad(self): - if self.rfc: - return "2%04d" % self.rfc.rfc_number - elif self.id.draft_status == "Active": - if self.id.in_ietf_process(): - return "11%02d" % (self.id.ietf_process._idinternal.cur_state_id) - else: - return "10" - else: - return "3" - -# --------------------------------------------------------------------------- - -class BallotWrapper: - _idinternal = None - ballot = None - ballot_active = False - _positions = None - position_values = ["Discuss", "Yes", "No Objection", "Abstain", "Recuse", "No Record"] - - def __init__(self, idinternal): - self._idinternal = idinternal - self.ballot = idinternal.ballot - if not idinternal.rfc_flag: - self.ballot_active = self.ballot.ballot_issued and (str(idinternal.cur_state) in BALLOT_ACTIVE_STATES) and str(idinternal.draft.status)=="Active"; - else: - self.ballot_active = self.ballot.ballot_issued and (str(idinternal.cur_state) in BALLOT_ACTIVE_STATES) - self._ballot_set = None - - def approval_text(self): - return self.ballot.approval_text - def ballot_writeup(self): - return self.ballot.ballot_writeup - def is_active(self): - return self.ballot_active - def ballot_id(self): - return self._idinternal.ballot_id - def was_deferred(self): - return self.ballot.defer - def deferred_by(self): - return self.ballot.defer_by - def deferred_date(self): - return self.ballot.defer_date - def is_ballot_set(self): - if not self._ballot_set: - self._ballot_set = self._idinternal.ballot_set() - return len(list(self._ballot_set)) > 1 - def ballot_set_other(self): - if not self.is_ballot_set(): - return [] - else: - return self._ballot_set.exclude(draft=self._idinternal) - - def _init(self): - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - self.old_init() - return - - from ietf.person.models import Person - from ietf.doc.models import BallotPositionDocEvent, NewRevisionDocEvent, BallotDocEvent - - active_ads = Person.objects.filter(role__name="ad", role__group__state="active").distinct() - - positions = [] - seen = {} - - new_revisions = list(NewRevisionDocEvent.objects.filter(doc=self.ballot, type="new_revision").order_by('-time', '-id')) - - ballot = self.ballot.latest_event(BallotDocEvent, type="created_ballot") - - for pos in BallotPositionDocEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", ballot=ballot).select_related('ad').order_by("-time", '-id'): - if pos.ad not in seen: - p = dict(ad_name=pos.ad.plain_name(), - ad_username=pos.ad.pk, # ought to rename this in doc_ballot_list - position=pos.pos.name, - is_old_ad=pos.ad not in active_ads, - old_positions=[]) - - rev = pos.doc.rev - for n in new_revisions: - if n.time <= pos.time: - rev = n.rev - break - - if pos.pos.slug == "discuss": - p["has_text"] = True - p["discuss_text"] = pos.discuss - p["discuss_date"] = pos.discuss_time.date() - p["discuss_revision"] = rev - - if pos.comment: - p["has_text"] = True - p["comment_text"] = pos.comment - p["comment_date"] = pos.comment_time.date() - p["comment_revision"] = rev - - positions.append(p) - seen[pos.ad] = p - else: - latest = seen[pos.ad] - if latest["old_positions"]: - prev = latest["old_positions"][-1] - else: - prev = latest["position"] - - if prev != pos.pos.name: - seen[pos.ad]["old_positions"].append(pos.pos.name) - - # add any missing ADs as No Record - if self.ballot_active: - for ad in active_ads: - if ad not in seen: - d = dict(ad_name=ad.plain_name(), - ad_username=ad.pk, - position="No Record", - ) - positions.append(d) - - self._positions = positions - - def old_init(self): - try: - ads = set() - except NameError: - # for Python 2.3 - from sets import Set as set - ads = set() - - positions = [] - all_comments = self.ballot.comments.all().select_related('ad') - for p in self.ballot.positions.all().select_related('ad'): - po = create_position_object(self.ballot, p, all_comments) - #if not self.ballot_active: - # if 'is_old_ad' in po: - # del po['is_old_ad'] - ads.add(str(p.ad)) - positions.append(po) - for c in all_comments: - if (str(c.ad) not in ads) and c.ad.is_current_ad(): - positions.append({'has_text':True, - 'comment_text':c.text, - 'comment_date':c.date, - 'comment_revision':str(c.revision), - 'ad_name':str(c.ad), - 'ad_username': c.ad.login_name, - 'position':'No Record', - 'is_old_ad':False}) - ads.add(str(c.ad)) - if self.ballot_active: - for ad in IESGLogin.active_iesg(): - if str(ad) not in ads: - positions.append(dict(ad_name=str(ad), - ad_username=ad.login_name, - position="No Record")) - self._positions = positions - - def position_for_ad(self, ad_name): - pl = self.position_list() - for p in pl: - if p["ad_name"] == ad_name: - return p["position"] - return None - - def position_list(self): - if not self._positions: - self._init() - return self._positions - - def get(self, v): - return [p for p in self.position_list() if p['position']==v] - - def get_discuss(self): - return self.get("Discuss") - def get_yes(self): - return self.get("Yes") - def get_no_objection(self): - return self.get("No Objection") - def get_abstain(self): - return self.get("Abstain") - def get_recuse(self): - return self.get("Recuse") - def get_no_record(self): - return self.get("No Record") - - def get_texts(self): - return [p for p in self.position_list() if ('has_text' in p) and p['has_text']] - - def dict(self): - summary = {} - for key in self.position_values: - tag = key.lower().replace(" ", "_") - summary[tag] = [ pos["ad_name"] for pos in self.get(key) ] - positions = self.position_list() - for i in range(len(positions)): - for key in ["comment_date", "discuss_date", ]: - if key in positions[i]: - positions[i][key] = positions[i][key].strftime("%Y-%m-%d") - return { - "active": self.is_active(), - "approval_text": self.approval_text(), - "ballot_writeup": self.ballot_writeup(), - "ballot_id": self.ballot_id(), - "deferred_by": unicode(self.deferred_by()), - "deferred_date": self.deferred_date() and self.deferred_date().strftime("%Y-%m-%d") , - "positions": positions, - "summary": summary, - "was_deferred": self.was_deferred(), - } - -def position_to_string(position): - positions = {"yes":"Yes", - "noobj":"No Objection", - "discuss":"Discuss", - "abstain":"Abstain", - "recuse":"Recuse"} - if not position: - return "No Record" - p = None - for k,v in positions.iteritems(): - if getattr(position, k) > 0: - p = v - if not p: - p = "No Record" - return p - -def create_position_object(ballot, position, all_comments): - positions = {"yes":"Yes", - "noobj":"No Objection", - "discuss":"Discuss", - "abstain":"Abstain", - "recuse":"Recuse"} - p = None - for k,v in positions.iteritems(): - if position.__dict__[k] > 0: - p = v - if not p: - p = "No Record" - r = dict(ad_name=str(position.ad), - ad_username=position.ad.login_name, - position=p) - if not position.ad.is_current_ad(): - r['is_old_ad'] = True - else: - r['is_old_ad'] = False - - was = [v for k,v in positions.iteritems() if position.__dict__[k] < 0] - if len(was) > 0: - r['old_positions'] = was - - comment = None - for c in all_comments: - if c.ad == position.ad: - comment = c - break - if comment and comment.text: - r['has_text'] = True - r['comment_text'] = comment.text - r['comment_date'] = comment.date - r['comment_revision'] = str(comment.revision) - - if p == "Discuss": - try: - discuss = ballot.discusses.get(ad=position.ad) - if discuss.text: - r['discuss_text'] = discuss.text - else: - r['discuss_text'] = '(empty)' - r['discuss_revision'] = str(discuss.revision) - r['discuss_date'] = discuss.date - except IESGDiscuss.DoesNotExist: - # this should never happen, but unfortunately it does - # fill in something to keep other parts of the code happy - r['discuss_text'] = "(error: discuss text not found)" - r['discuss_revision'] = "00" - r['discuss_date'] = date(2000, 1,1) - r['has_text'] = True - return r - diff --git a/ietf/idrfc/mirror_draft_versions.py b/ietf/idrfc/mirror_draft_versions.py deleted file mode 100644 index faffe5c35..000000000 --- a/ietf/idrfc/mirror_draft_versions.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -# All rights reserved. Contact: Pasi Eronen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the Nokia Corporation and/or its -# subsidiary(-ies) nor the names of its contributors may be used -# to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from ietf import settings -from django.core import management -management.setup_environ(settings) -from django import db - -import urllib2 -from datetime import datetime -import socket -import sys - -URL = "http://merlot.tools.ietf.org/~pasi/draft_versions.txt" -TABLE = "draft_versions_mirror" - -log_data = "" -def log(line): - global log_data - if len(sys.argv) > 1: - print line - else: - log_data += line + "\n" - -try: - log("output from mirror_draft_versions.py:\n") - log("time: "+str(datetime.now())) - log("host: "+socket.gethostname()) - log("url: "+URL) - - log("downloading...") - socket.setdefaulttimeout(30) - response = urllib2.urlopen(URL) - #log("got \n"+str(response.info())) - log("parsing...") - data = [] - for line in response.readlines(): - rec = line[:-1].split("\t") - data.append(rec) - - log("got " + str(len(data)) + " entries") - if len(data) < 10000: - raise Exception('not enough data') - - log("connecting to database...") - cursor = db.connection.cursor() - log("removing old data...") - cursor.execute("DELETE FROM "+TABLE) - log("inserting new data...") - cursor.executemany("INSERT INTO "+TABLE+" (filename, revision, revision_date) VALUES (%s, %s, %s)", data) - cursor.close() - db.connection._commit() - db.connection.close() - - log("all done!") - log_data = "" -finally: - if len(log_data) > 0: - print log_data diff --git a/ietf/idrfc/models.py b/ietf/idrfc/models.py deleted file mode 100644 index 7687eb2b0..000000000 --- a/ietf/idrfc/models.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). -# All rights reserved. Contact: Pasi Eronen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the Nokia Corporation and/or its -# subsidiary(-ies) nor the names of its contributors may be used -# to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from django.db import models -from ietf.idtracker.models import InternetDraft - -class RfcEditorQueue(models.Model): - STREAM_CHOICES = ( - (0, 'Unknown'), - (1, 'IETF'), - (2, 'IAB'), - (3, 'IRTF'), - (4, 'Independent') - ) - draft = models.OneToOneField(InternetDraft, db_column="id_document_tag", related_name="rfc_editor_queue_state",primary_key=True) - date_received = models.DateField() - state = models.CharField(max_length=200, blank=True, null=True) - # currently, queue2.xml does not have this information, so - # this field will be NULL (but we could get it from other sources) - state_date = models.DateField(blank=True,null=True) - stream = models.IntegerField(choices=STREAM_CHOICES) - auth48_url = models.CharField(max_length=200, blank=True, null=True) - rfc_number = models.IntegerField(null=True) - def __str__(self): - return "RfcEditorQueue"+str([self.draft, self.date_received, self.state, self.state_date, self.stream]) - class Meta: - db_table = "rfc_editor_queue_mirror" - -class RfcEditorQueueRef(models.Model): - source = models.ForeignKey(InternetDraft, db_column="source", related_name="rfc_editor_queue_refs") - destination = models.CharField(max_length=200) - in_queue = models.BooleanField() - direct = models.BooleanField() # Is this a direct (or indirect) depencency? - class Meta: - db_table = "rfc_editor_queue_mirror_refs" - -class RfcIndex(models.Model): - rfc_number = models.IntegerField(primary_key=True) - title = models.CharField(max_length=250) - authors = models.CharField(max_length=250) - rfc_published_date = models.DateField() - current_status = models.CharField(max_length=50,null=True) - updates = models.CharField(max_length=200,blank=True,null=True) - updated_by = models.CharField(max_length=200,blank=True,null=True) - obsoletes = models.CharField(max_length=200,blank=True,null=True) - obsoleted_by = models.CharField(max_length=200,blank=True,null=True) - also = models.CharField(max_length=50,blank=True,null=True) - draft = models.CharField(max_length=200,null=True) - has_errata = models.BooleanField() - stream = models.CharField(max_length=15,blank=True,null=True) - wg = models.CharField(max_length=15,blank=True,null=True) - file_formats = models.CharField(max_length=20,blank=True,null=True) - def __str__(self): - return "RfcIndex"+str(self.rfc_number) - class Meta: - db_table = "rfc_index_mirror" - -class DraftVersions(models.Model): - # Django does not support multi-column primary keys, so - # we can't use filename+revision. But the key for this table - # does not really matter, so we'll have an 'id' field - id = models.AutoField(primary_key=True) - filename = models.CharField(max_length=200, db_index=True) - revision = models.CharField(max_length=2) - revision_date = models.DateField() - def __str__(self): - return "DraftVersions"+self.filename+self.revision+str(self.revision_date) - class Meta: - db_table = "draft_versions_mirror" - - -from django.conf import settings -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - RfcIndexOld = RfcIndex - from ietf.doc.proxy import RfcIndex diff --git a/ietf/settings.py b/ietf/settings.py index f420ac2dc..fb9e2107d 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -170,7 +170,6 @@ INSTALLED_APPS = ( 'ietf.meeting', #'ietf.proceedings', 'ietf.redirects', - 'ietf.idrfc', 'ietf.wginfo', 'ietf.submit', 'ietf.wgcharter', From 12722a68fa4b43d8ac20e3626d632cc67215a089 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 19 Nov 2013 16:14:27 +0000 Subject: [PATCH 115/173] Remove backtrace email code from submit __init__.py, after the submit revamp I think I am responsible for all remaining bugs and not esanchez@yaco.es, and the code sometimes annoyingly interferes (through a bug in admin) with loading the submit module in the shell - Legacy-Id: 6722 --- ietf/submit/__init__.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/ietf/submit/__init__.py b/ietf/submit/__init__.py index 101ca60a0..e69de29bb 100644 --- a/ietf/submit/__init__.py +++ b/ietf/submit/__init__.py @@ -1,18 +0,0 @@ -# coding: latin-1 - -from types import ModuleType -import urls, models, views, forms, utils, admin - -# These people will be sent a stack trace if there's an uncaught exception in -# code any of the modules imported above: -DEBUG_EMAILS = [ - ('Emilio A. Sánchez', 'esanchez@yaco.es'), -] - -for k in locals().keys(): - m = locals()[k] - if isinstance(m, ModuleType): - if hasattr(m, "DEBUG_EMAILS"): - DEBUG_EMAILS += list(getattr(m, "DEBUG_EMAILS")) - setattr(m, "DEBUG_EMAILS", DEBUG_EMAILS) - From f9d400ae5cffc97f1c1e7dafdb7e3172fb7df443 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 27 Nov 2013 13:15:52 +0000 Subject: [PATCH 116/173] Move decorator import in debug.py closer to actual usage points so the decorator library is not imported unless needed - it doesn't appear to be part of Python 2.7 so needs to be installed separately - Legacy-Id: 6765 --- debug.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/debug.py b/debug.py index a172bb521..fbcbc61f7 100644 --- a/debug.py +++ b/debug.py @@ -24,8 +24,6 @@ try: except ImportError: debug = True -from decorator import decorator - # A debug decorator, written by Paul Butler, taken from # http://paulbutler.org/archives/python-debugging-with-decorators/ # Additional functions and decorator functionality added by @@ -78,6 +76,7 @@ def trace(fn): # renamed from 'report' by henrik 16 Jun 2011 return ret wrap.callcount = 0 if debug: + from decorator import decorator return decorator(wrap, fn) else: return fn @@ -109,6 +108,7 @@ def time(fn): return ret wrap.callcount = 0 if debug: + from decorator import decorator return decorator(wrap, fn) else: return fn @@ -169,6 +169,7 @@ def profile(fn): prof.dump_stats(datafn) return retval if debug: + from decorator import decorator return decorator(wrapper, fn) else: return fn From 2cf72aa9e5ec4278587f0db7fbf1c17af69184c4 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 27 Nov 2013 13:19:58 +0000 Subject: [PATCH 117/173] Remove references to names.xml in perma_fixtures, they are not needed anyway - Legacy-Id: 6766 --- ietf/meeting/tests/agenda.py | 3 +-- ietf/meeting/tests/api.py | 2 +- ietf/meeting/tests/auths.py | 2 +- ietf/meeting/tests/edit.py | 2 +- ietf/meeting/tests/urlgen.py | 2 +- ietf/meeting/tests/view.py | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ietf/meeting/tests/agenda.py b/ietf/meeting/tests/agenda.py index a8d513b5f..7c9316a3c 100644 --- a/ietf/meeting/tests/agenda.py +++ b/ietf/meeting/tests/agenda.py @@ -12,8 +12,7 @@ import debug class AgendaInfoTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = [ 'names.xml', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName - 'meeting83.json', + perma_fixtures = [ 'meeting83.json', 'constraint83.json', 'workinggroups.json', 'groupgroup.json', diff --git a/ietf/meeting/tests/api.py b/ietf/meeting/tests/api.py index bbcaa152a..6d4eaa260 100644 --- a/ietf/meeting/tests/api.py +++ b/ietf/meeting/tests/api.py @@ -15,7 +15,7 @@ import debug class ApiTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = [ 'names.xml', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName + perma_fixtures = [ 'meeting83.json', 'constraint83.json', 'workinggroups.json', diff --git a/ietf/meeting/tests/auths.py b/ietf/meeting/tests/auths.py index 6dda45e80..3c7ff0fd6 100644 --- a/ietf/meeting/tests/auths.py +++ b/ietf/meeting/tests/auths.py @@ -20,7 +20,7 @@ auth_ferrel = {'REMOTE_USER':'stephen.farrell@cs.tcd.ie'} class AuthDataTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = [ 'names.xml', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName + perma_fixtures = [ 'meeting83.json', 'constraint83.json', 'workinggroups.json', diff --git a/ietf/meeting/tests/edit.py b/ietf/meeting/tests/edit.py index 0fdf10cca..5206a8755 100644 --- a/ietf/meeting/tests/edit.py +++ b/ietf/meeting/tests/edit.py @@ -13,7 +13,7 @@ capture_output = False class EditTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = [ 'names.xml', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName + perma_fixtures = [ 'meeting83.json', 'constraint83.json', 'workinggroups.json', diff --git a/ietf/meeting/tests/urlgen.py b/ietf/meeting/tests/urlgen.py index 7c1624d3a..5c6d45404 100644 --- a/ietf/meeting/tests/urlgen.py +++ b/ietf/meeting/tests/urlgen.py @@ -14,7 +14,7 @@ from ietf.group.models import Group class UrlGenTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = [ 'names.xml', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName + perma_fixtures = [ 'meeting83.json', 'constraint83.json', 'workinggroups.json', diff --git a/ietf/meeting/tests/view.py b/ietf/meeting/tests/view.py index 07f2b491b..2eeb155e9 100644 --- a/ietf/meeting/tests/view.py +++ b/ietf/meeting/tests/view.py @@ -13,7 +13,7 @@ from ietf.meeting.views import edit_agenda class ViewTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = [ 'names', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName + perma_fixtures = [ 'meeting83', 'constraint83', 'workinggroups', From 35939cc20c69110c481b04def90b121b3538c8e3 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 27 Nov 2013 16:45:05 +0000 Subject: [PATCH 118/173] Remove references to old announcement models and remove the model definitions - Legacy-Id: 6767 --- ietf/announcements/models.py | 87 -------------------------------- ietf/announcements/sitemaps.py | 5 +- ietf/announcements/tests.py | 53 ------------------- ietf/announcements/urls.py | 8 +-- ietf/announcements/views.py | 33 +----------- ietf/bin/send-scheduled-mail | 33 ++++-------- ietf/secr/announcement/forms.py | 1 - ietf/secr/announcement/models.py | 1 - 8 files changed, 14 insertions(+), 207 deletions(-) diff --git a/ietf/announcements/models.py b/ietf/announcements/models.py index 6ee8e1bfb..137941ffa 100644 --- a/ietf/announcements/models.py +++ b/ietf/announcements/models.py @@ -1,88 +1 @@ -# Copyright The IETF Trust 2007, All Rights Reserved - from django.db import models -from django.conf import settings -from ietf.idtracker.models import PersonOrOrgInfo, ChairsHistory -#from django.contrib.auth.models import Permission - -# I don't know why the IETF database mostly stores times -# as char(N) instead of TIME. Until it's important, let's -# keep them as char here too. - -# email is not used; the announced_from text is Foo Bar -class AnnouncedFrom(models.Model): - announced_from_id = models.AutoField(primary_key=True) - announced_from = models.CharField(blank=True, max_length=255, db_column='announced_from_value') - email = models.CharField(blank=True, max_length=255, db_column='announced_from_email', editable=False) - #permission = models.ManyToManyField(Permission, limit_choices_to={'codename__endswith':'announcedfromperm'}, verbose_name='Permission Required', blank=True) - def __str__(self): - return self.announced_from - class Meta: - db_table = 'announced_from' - #permissions = ( - # ("ietf_chair_announcedfromperm", "Can send messages from IETF Chair"), - # ("iab_chair_announcedfromperm", "Can send messages from IAB Chair"), - # ("iad_announcedfromperm", "Can send messages from IAD"), - # ("ietf_execdir_announcedfromperm", "Can send messages from IETF Executive Director"), - # ("other_announcedfromperm", "Can send announcements from others"), - #) - -class AnnouncedTo(models.Model): - announced_to_id = models.AutoField(primary_key=True) - announced_to = models.CharField(blank=True, max_length=255, db_column='announced_to_value') - email = models.CharField(blank=True, max_length=255, db_column='announced_to_email') - def __str__(self): - return self.announced_to - class Meta: - db_table = 'announced_to' - -class Announcement(models.Model): - announcement_id = models.AutoField(primary_key=True) - announced_by = models.ForeignKey(PersonOrOrgInfo, db_column='announced_by') - announced_date = models.DateField(null=True, blank=True) - announced_time = models.CharField(blank=True, max_length=20) - text = models.TextField(blank=True, db_column='announcement_text') - announced_from = models.ForeignKey(AnnouncedFrom) - cc = models.CharField(blank=True, null=True, max_length=255) - subject = models.CharField(blank=True, max_length=255) - extra = models.TextField(blank=True,null=True) - announced_to = models.ForeignKey(AnnouncedTo) - nomcom = models.NullBooleanField() - nomcom_chair = models.ForeignKey(ChairsHistory, null=True, blank=True) - manually_added = models.BooleanField(db_column='manualy_added') - other_val = models.CharField(blank=True, null=True, max_length=255) - def __str__(self): - return "Announcement from %s to %s on %s %s" % (self.announced_from, self.announced_to, self.announced_date, self.announced_time) - def from_name(self): - if self.announced_from_id == 99: - return self.other_val - if self.announced_from_id == 18: # sigh hardcoding - return self.nomcom_chair.person - return self.announced_from - class Meta: - db_table = 'announcements' - -class ScheduledAnnouncement(models.Model): - mail_sent = models.BooleanField() - to_be_sent_date = models.DateField(null=True, blank=True) - to_be_sent_time = models.CharField(blank=True, null=True, max_length=50) - scheduled_by = models.CharField(blank=True, max_length=100) - scheduled_date = models.DateField(null=True, blank=True) - scheduled_time = models.CharField(blank=True, max_length=50) - subject = models.CharField(blank=True, max_length=255) - to_val = models.CharField(blank=True, max_length=255) - from_val = models.CharField(blank=True, max_length=255) - cc_val = models.TextField(blank=True,null=True) - body = models.TextField(blank=True) - actual_sent_date = models.DateField(null=True, blank=True) - actual_sent_time = models.CharField(blank=True, max_length=50) # should be time, but database contains oddities - first_q = models.IntegerField(null=True, blank=True) - second_q = models.IntegerField(null=True, blank=True) - note = models.TextField(blank=True,null=True) - content_type = models.CharField(blank=True, max_length=255) - replyto = models.CharField(blank=True, null=True, max_length=255) - bcc_val = models.CharField(blank=True, null=True, max_length=255) - def __str__(self): - return "Scheduled Announcement from %s to %s on %s %s" % (self.from_val, self.to_val, self.to_be_sent_date, self.to_be_sent_time) - class Meta: - db_table = 'scheduled_announcements' diff --git a/ietf/announcements/sitemaps.py b/ietf/announcements/sitemaps.py index 2765f0fa0..9cf648cf7 100644 --- a/ietf/announcements/sitemaps.py +++ b/ietf/announcements/sitemaps.py @@ -2,15 +2,12 @@ from django.conf import settings from django.contrib.sitemaps import Sitemap -from ietf.announcements.models import Announcement from ietf.message.models import Message class NOMCOMAnnouncementsMap(Sitemap): changefreq = "never" def items(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return Message.objects.filter(related_groups__acronym__startswith="nomcom").exclude(related_groups__acronym="nomcom").order_by('-time') - return Announcement.objects.all().filter(nomcom=True) + return Message.objects.filter(related_groups__acronym__startswith="nomcom").exclude(related_groups__acronym="nomcom").order_by('-time') def location(self, obj): return "/ann/nomcom/%d/" % obj.id def lastmod(self, obj): diff --git a/ietf/announcements/tests.py b/ietf/announcements/tests.py index f23a0628d..2b8c0954d 100644 --- a/ietf/announcements/tests.py +++ b/ietf/announcements/tests.py @@ -7,8 +7,6 @@ from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox from ietf.utils import TestCase -from ietf.announcements.models import ScheduledAnnouncement - from ietf.message.models import Message, SendQueue from ietf.person.models import Person @@ -22,54 +20,6 @@ class AnnouncementsUrlTestCase(SimpleUrlTestCase): return content class SendScheduledAnnouncementsTestCase(TestCase): - def test_send_plain_announcement(self): - a = ScheduledAnnouncement.objects.create( - mail_sent=False, - subject="This is a test", - to_val="test@example.com", - from_val="testmonkey@example.com", - cc_val="cc.a@example.com, cc.b@example.com", - bcc_val="bcc@example.com", - body="Hello World!", - content_type="", - ) - - mailbox_before = len(outbox) - - from ietf.announcements.send_scheduled import send_scheduled_announcement - send_scheduled_announcement(a) - - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("This is a test" in outbox[-1]["Subject"]) - self.assertTrue(ScheduledAnnouncement.objects.get(id=a.id).mail_sent) - - def test_send_mime_announcement(self): - a = ScheduledAnnouncement.objects.create( - mail_sent=False, - subject="This is a test", - to_val="test@example.com", - from_val="testmonkey@example.com", - cc_val="cc.a@example.com, cc.b@example.com", - bcc_val="bcc@example.com", - body='--NextPart\r\n\r\nA New Internet-Draft is available from the on-line Internet-Drafts directories.\r\n--NextPart\r\nContent-Type: Message/External-body;\r\n\tname="draft-huang-behave-bih-01.txt";\r\n\tsite="ftp.ietf.org";\r\n\taccess-type="anon-ftp";\r\n\tdirectory="internet-drafts"\r\n\r\nContent-Type: text/plain\r\nContent-ID: <2010-07-30001541.I-D@ietf.org>\r\n\r\n--NextPart--', - content_type='Multipart/Mixed; Boundary="NextPart"', - ) - - mailbox_before = len(outbox) - - from ietf.announcements.send_scheduled import send_scheduled_announcement - send_scheduled_announcement(a) - - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("This is a test" in outbox[-1]["Subject"]) - self.assertTrue("--NextPart" in outbox[-1].as_string()) - self.assertTrue(ScheduledAnnouncement.objects.get(id=a.id).mail_sent) - - -class SendScheduledAnnouncementsTestCaseREDESIGN(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ["names"] - def test_send_plain_announcement(self): make_test_data() @@ -128,6 +78,3 @@ class SendScheduledAnnouncementsTestCaseREDESIGN(TestCase): self.assertTrue("This is a test" in outbox[-1]["Subject"]) self.assertTrue("--NextPart" in outbox[-1].as_string()) self.assertTrue(SendQueue.objects.get(id=q.id).sent_at) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - SendScheduledAnnouncementsTestCase = SendScheduledAnnouncementsTestCaseREDESIGN diff --git a/ietf/announcements/urls.py b/ietf/announcements/urls.py index 0f101a17f..9790002e3 100644 --- a/ietf/announcements/urls.py +++ b/ietf/announcements/urls.py @@ -1,16 +1,10 @@ # Copyright The IETF Trust 2007, All Rights Reserved from django.conf.urls.defaults import patterns -from ietf.announcements.models import Announcement from django.conf import settings -nomcom_dict = { - 'queryset': Announcement.objects.all().filter(nomcom=True) - } - urlpatterns = patterns('', -# (r'^nomcom/$', 'django.views.generic.simple.redirect_to', {'url': 'http://www.ietf.org/nomcom/index.html'} ), (r'^nomcom/$', 'ietf.announcements.views.nomcom'), - (r'^nomcom/(?P\d+)/$', 'ietf.announcements.views.message_detail') if settings.USE_DB_REDESIGN_PROXY_CLASSES else (r'^nomcom/(?P\d+)/$', 'django.views.generic.list_detail.object_detail', nomcom_dict) + (r'^nomcom/(?P\d+)/$', 'ietf.announcements.views.message_detail')) ) diff --git a/ietf/announcements/views.py b/ietf/announcements/views.py index 45094beb9..166ec2e8d 100644 --- a/ietf/announcements/views.py +++ b/ietf/announcements/views.py @@ -1,42 +1,18 @@ # Copyright The IETF Trust 2007, All Rights Reserved +import re + from django.views.generic.simple import direct_to_template from django.shortcuts import get_object_or_404 from django.conf import settings from django.db.models import Q -import re - from ietf.idtracker.models import ChairsHistory from ietf.idtracker.models import Role -from ietf.announcements.models import Announcement from ietf.group.models import Group, GroupEvent from ietf.message.models import Message def nomcom(request): - curr_chair = (ChairsHistory.objects. - get(chair_type=Role.NOMCOM_CHAIR, present_chair='1')) - - all_chairs = (ChairsHistory.objects.all(). - filter(chair_type='3',start_year__gt = 2003). - order_by('-start_year')) - - nomcom_announcements = Announcement.objects.all().filter(nomcom=1) - - regimes = [] - - for chair in all_chairs: - chair_announcements = (nomcom_announcements.filter(nomcom_chair=chair). - order_by('-announced_date','-announced_time')) - regimes = regimes + [{'chair': chair, - 'announcements' : chair_announcements }] - - return direct_to_template(request, - "announcements/nomcom.html", - { 'curr_chair' : curr_chair, - 'regimes' : regimes }) - -def nomcomREDESIGN(request): address_re = re.compile("<.*>") nomcoms = list(Group.objects.filter(acronym__startswith="nomcom").exclude(name="nomcom")) @@ -69,11 +45,6 @@ def nomcomREDESIGN(request): "announcements/nomcomREDESIGN.html", { 'curr_chair' : regimes[0]["chair"], 'regimes' : regimes }) - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - nomcom = nomcomREDESIGN - def message_detail(request, object_id): # restrict to nomcom announcements for the time being diff --git a/ietf/bin/send-scheduled-mail b/ietf/bin/send-scheduled-mail index b29f2a425..b568643ee 100755 --- a/ietf/bin/send-scheduled-mail +++ b/ietf/bin/send-scheduled-mail @@ -10,7 +10,6 @@ management.setup_environ(settings) syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER) -from ietf.announcements.models import ScheduledAnnouncement from ietf.announcements.send_scheduled import * from django.db.models import Q @@ -25,27 +24,15 @@ mode = sys.argv[1] now = datetime.datetime.now() -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.announcements.models import SendQueue - announcements = SendQueue.objects.filter(sent_at=None) - if mode == "rsync": - announcements = announcements.filter(send_at=None) - elif mode == "specific": - announcements = announcements.exclude(send_at=None).filter(send_at__lte=now) -else: - announcements = ScheduledAnnouncement.objects.filter(mail_sent=False) - if mode == "rsync": - # include bogus 0000-00-00 entries - announcements = announcements.filter(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min)) - elif mode == "specific": - # exclude null/bogus entries - announcements = announcements.exclude(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min)) +from ietf.message.models import SendQueue +needs_sending = SendQueue.objects.filter(sent_at=None).select_related("message") +if mode == "rsync": + needs_sending = needs_sending.filter(send_at=None) +elif mode == "specific": + needs_sending = needs_sending.exclude(send_at=None).filter(send_at__lte=now) - announcements = announcements.filter(to_be_sent_date__lte=now.date(), - to_be_sent_time__lte=now.time()) - -for announcement in announcements: - send_scheduled_announcement(announcement) +for s in needs_sending: + send_scheduled_announcement(s) - subject = announcement.message.subject if settings.USE_DB_REDESIGN_PROXY_CLASSES else announcement.subject - syslog.syslog(u'Sent scheduled announcement %s "%s"' % (announcement.id, subject)) + subject = s.message.subject + syslog.syslog(u'Sent scheduled announcement %s "%s"' % (s.id, subject)) diff --git a/ietf/secr/announcement/forms.py b/ietf/secr/announcement/forms.py index 0dab4343a..8486ce49f 100644 --- a/ietf/secr/announcement/forms.py +++ b/ietf/secr/announcement/forms.py @@ -135,7 +135,6 @@ def get_to_choices(): # --------------------------------------------- # Select Choices # --------------------------------------------- -#TO_CHOICES = tuple(AnnouncedTo.objects.values_list('announced_to_id','announced_to')) TO_CHOICES = get_to_choices() #FROM_CHOICES = get_from_choices() diff --git a/ietf/secr/announcement/models.py b/ietf/secr/announcement/models.py index 948c3a258..d3855c5b0 100644 --- a/ietf/secr/announcement/models.py +++ b/ietf/secr/announcement/models.py @@ -1,4 +1,3 @@ from django.db import models -from ietf.announcements.models import AnnouncedTo #from ietf.message.models import Message from ietf.group.models import Group, Role From 78d9bfff246b580e9cf33d0f74aee7c7caf8891f Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 28 Nov 2013 13:05:51 +0000 Subject: [PATCH 119/173] Move send_scheduled_announcement functionality to messages/utils.py - Legacy-Id: 6768 --- ietf/announcements/send_scheduled.py | 68 ---------------------------- ietf/announcements/tests.py | 60 ------------------------ ietf/announcements/urls.py | 2 +- ietf/bin/send-scheduled-mail | 19 +++----- ietf/message/utils.py | 40 ++++++++++++++-- ietf/secr/drafts/email.py | 4 +- 6 files changed, 47 insertions(+), 146 deletions(-) delete mode 100644 ietf/announcements/send_scheduled.py diff --git a/ietf/announcements/send_scheduled.py b/ietf/announcements/send_scheduled.py deleted file mode 100644 index 0a8b91093..000000000 --- a/ietf/announcements/send_scheduled.py +++ /dev/null @@ -1,68 +0,0 @@ -import re, datetime, email - -from django.conf import settings - -from ietf.utils.mail import send_mail_text, send_mail_mime - -first_dot_on_line_re = re.compile(r'^\.', re.MULTILINE) - -def send_scheduled_announcement(announcement): - # for some reason, the old Perl code base substituted away . on line starts - body = first_dot_on_line_re.sub("", announcement.body) - - extra = {} - if announcement.replyto: - extra['Reply-To'] = announcement.replyto - - # announcement.content_type can contain a case-sensitive parts separator, - # so we need to keep it as is, not lowercased, but we want a lowercased - # version for the coming comparisons. - content_type_lowercase = announcement.content_type.lower() - if not content_type_lowercase or 'text/plain' in content_type_lowercase: - send_mail_text(None, announcement.to_val, announcement.from_val, announcement.subject, - body, cc=announcement.cc_val, bcc=announcement.bcc_val) - elif 'multipart/' in content_type_lowercase: - # make body a real message so we can parse it. - body = ("MIME-Version: 1.0\r\nContent-Type: %s\r\n" % announcement.content_type) + body - - msg = email.message_from_string(body.encode("utf-8")) - send_mail_mime(None, announcement.to_val, announcement.from_val, announcement.subject, - msg, cc=announcement.cc_val, bcc=announcement.bcc_val) - - now = datetime.datetime.now() - announcement.actual_sent_date = now.date() - announcement.actual_sent_time = str(now.time()) - announcement.mail_sent = True - announcement.save() - - -def send_scheduled_announcementREDESIGN(send_queue): - message = send_queue.message - - # for some reason, the old Perl code base substituted away . on line starts - body = first_dot_on_line_re.sub("", message.body) - - extra = {} - if message.reply_to: - extra['Reply-To'] = message.reply_to - - # announcement.content_type can contain a case-sensitive parts separator, - # so we need to keep it as is, not lowercased, but we want a lowercased - # version for the coming comparisons. - content_type_lowercase = message.content_type.lower() - if not content_type_lowercase or 'text/plain' in content_type_lowercase: - send_mail_text(None, message.to, message.frm, message.subject, - body, cc=message.cc, bcc=message.bcc) - elif 'multipart' in content_type_lowercase: - # make body a real message so we can parse it - body = ("MIME-Version: 1.0\r\nContent-Type: %s\r\n" % message.content_type) + body - - msg = email.message_from_string(body.encode("utf-8")) - send_mail_mime(None, message.to, message.frm, message.subject, - msg, cc=message.cc, bcc=message.bcc) - - send_queue.sent_at = datetime.datetime.now() - send_queue.save() - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - send_scheduled_announcement = send_scheduled_announcementREDESIGN diff --git a/ietf/announcements/tests.py b/ietf/announcements/tests.py index 2b8c0954d..e6ec9226a 100644 --- a/ietf/announcements/tests.py +++ b/ietf/announcements/tests.py @@ -18,63 +18,3 @@ class AnnouncementsUrlTestCase(SimpleUrlTestCase): return canonicalize_sitemap(content) else: return content - -class SendScheduledAnnouncementsTestCase(TestCase): - def test_send_plain_announcement(self): - make_test_data() - - msg = Message.objects.create( - by=Person.objects.get(name="(System)"), - subject="This is a test", - to="test@example.com", - frm="testmonkey@example.com", - cc="cc.a@example.com, cc.b@example.com", - bcc="bcc@example.com", - body="Hello World!", - content_type="", - ) - - q = SendQueue.objects.create( - by=Person.objects.get(name="(System)"), - message=msg, - send_at=datetime.datetime.now() + datetime.timedelta(hours=12) - ) - - mailbox_before = len(outbox) - - from ietf.announcements.send_scheduled import send_scheduled_announcement - send_scheduled_announcement(q) - - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("This is a test" in outbox[-1]["Subject"]) - self.assertTrue(SendQueue.objects.get(id=q.id).sent_at) - - def test_send_mime_announcement(self): - make_test_data() - - msg = Message.objects.create( - by=Person.objects.get(name="(System)"), - subject="This is a test", - to="test@example.com", - frm="testmonkey@example.com", - cc="cc.a@example.com, cc.b@example.com", - bcc="bcc@example.com", - body='--NextPart\r\n\r\nA New Internet-Draft is available from the on-line Internet-Drafts directories.\r\n--NextPart\r\nContent-Type: Message/External-body;\r\n\tname="draft-huang-behave-bih-01.txt";\r\n\tsite="ftp.ietf.org";\r\n\taccess-type="anon-ftp";\r\n\tdirectory="internet-drafts"\r\n\r\nContent-Type: text/plain\r\nContent-ID: <2010-07-30001541.I-D@ietf.org>\r\n\r\n--NextPart--', - content_type='Multipart/Mixed; Boundary="NextPart"', - ) - - q = SendQueue.objects.create( - by=Person.objects.get(name="(System)"), - message=msg, - send_at=datetime.datetime.now() + datetime.timedelta(hours=12) - ) - - mailbox_before = len(outbox) - - from ietf.announcements.send_scheduled import send_scheduled_announcement - send_scheduled_announcement(q) - - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("This is a test" in outbox[-1]["Subject"]) - self.assertTrue("--NextPart" in outbox[-1].as_string()) - self.assertTrue(SendQueue.objects.get(id=q.id).sent_at) diff --git a/ietf/announcements/urls.py b/ietf/announcements/urls.py index 9790002e3..f988e1785 100644 --- a/ietf/announcements/urls.py +++ b/ietf/announcements/urls.py @@ -6,5 +6,5 @@ from django.conf import settings urlpatterns = patterns('', (r'^nomcom/$', 'ietf.announcements.views.nomcom'), - (r'^nomcom/(?P\d+)/$', 'ietf.announcements.views.message_detail')) + (r'^nomcom/(?P\d+)/$', 'ietf.announcements.views.message_detail'), ) diff --git a/ietf/bin/send-scheduled-mail b/ietf/bin/send-scheduled-mail index b568643ee..882e8369c 100755 --- a/ietf/bin/send-scheduled-mail +++ b/ietf/bin/send-scheduled-mail @@ -10,29 +10,24 @@ management.setup_environ(settings) syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER) -from ietf.announcements.send_scheduled import * -from django.db.models import Q - if len(sys.argv) != 2 or sys.argv[1] not in ('all', 'rsync', 'specific'): - print "USAGE: %s " % os.path.basename(__file__) + print "USAGE: %s " % os.path.basename(__file__) print "'all' means all not sent" - print "'rsync' means all not sent where to-be-sent-date is null" print "'specific' means all not sent that are due to be sent" sys.exit(1) +from ietf.message.utils import send_scheduled_message_from_send_queue +from ietf.message.models import SendQueue + mode = sys.argv[1] now = datetime.datetime.now() -from ietf.message.models import SendQueue needs_sending = SendQueue.objects.filter(sent_at=None).select_related("message") -if mode == "rsync": - needs_sending = needs_sending.filter(send_at=None) -elif mode == "specific": +if mode == "specific": needs_sending = needs_sending.exclude(send_at=None).filter(send_at__lte=now) for s in needs_sending: - send_scheduled_announcement(s) + send_scheduled_message_from_send_queue(s) - subject = s.message.subject - syslog.syslog(u'Sent scheduled announcement %s "%s"' % (s.id, subject)) + syslog.syslog(u'Sent scheduled message %s "%s"' % (s.id, s.message.subject)) diff --git a/ietf/message/utils.py b/ietf/message/utils.py index 1fc2e5133..9d1e8ffe8 100644 --- a/ietf/message/utils.py +++ b/ietf/message/utils.py @@ -1,9 +1,14 @@ +import re, datetime, email + +from django.conf import settings + +from ietf.utils.mail import send_mail_text, send_mail_mime from ietf.message.models import Message -def infer_message(s): - from email import message_from_string +first_dot_on_line_re = re.compile(r'^\.', re.MULTILINE) - parsed = message_from_string(s.encode("utf-8")) +def infer_message(s): + parsed = email.message_from_string(s.encode("utf-8")) m = Message() m.subject = parsed.get("Subject", "").decode("utf-8") @@ -14,3 +19,32 @@ def infer_message(s): m.body = parsed.get_payload().decode("utf-8") return m + +def send_scheduled_message_from_send_queue(send_queue): + message = send_queue.message + + # for some reason, the old Perl code base substituted away . on line starts + body = first_dot_on_line_re.sub("", message.body) + + extra = {} + if message.reply_to: + extra['Reply-To'] = message.reply_to + + # announcement.content_type can contain a case-sensitive parts separator, + # so we need to keep it as is, not lowercased, but we want a lowercased + # version for the coming comparisons. + content_type_lowercase = message.content_type.lower() + if not content_type_lowercase or 'text/plain' in content_type_lowercase: + send_mail_text(None, message.to, message.frm, message.subject, + body, cc=message.cc, bcc=message.bcc) + elif 'multipart' in content_type_lowercase: + # make body a real message so we can parse it + body = ("MIME-Version: 1.0\r\nContent-Type: %s\r\n" % message.content_type) + body + + msg = email.message_from_string(body.encode("utf-8")) + send_mail_mime(None, message.to, message.frm, message.subject, + msg, cc=message.cc, bcc=message.bcc) + + send_queue.sent_at = datetime.datetime.now() + send_queue.save() + diff --git a/ietf/secr/drafts/email.py b/ietf/secr/drafts/email.py index 534f0578f..688400557 100644 --- a/ietf/secr/drafts/email.py +++ b/ietf/secr/drafts/email.py @@ -3,7 +3,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.template.loader import render_to_string from ietf.message.models import Message, SendQueue -from ietf.announcements.send_scheduled import send_scheduled_announcement +from ietf.message.utils import send_scheduled_message_from_send_queue from ietf.doc.models import DocumentAuthor from ietf.person.models import Person from ietf.secr.utils.document import get_start_date @@ -41,7 +41,7 @@ def announcement_from_form(data, **kwargs): send_queue = SendQueue.objects.create(by=by,message=message) # uncomment for testing - send_scheduled_announcement(send_queue) + send_scheduled_message_from_send_queue(send_queue) return message From d0f2859631a7e685b616100d52935b5b2db8d47d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 28 Nov 2013 17:55:10 +0000 Subject: [PATCH 120/173] Move NomCom announcements view to nomcom/, move message view to message/, add tests, add redirects, fix a couple of small bugs in the announcements view - Legacy-Id: 6769 --- ietf/announcements/models.py | 1 - ietf/announcements/urls.py | 10 ---- ietf/announcements/views.py | 59 ------------------- ietf/message/views.py | 15 +++++ ietf/nomcom/redirect_ann_urls.py | 7 +++ ietf/nomcom/test_data.py | 9 ++- ietf/nomcom/tests.py | 23 +++++++- ietf/nomcom/urls.py | 5 ++ ietf/nomcom/views.py | 42 ++++++++++++- ietf/settings.py | 1 - .../announcements/message_detail.html | 18 ------ ietf/templates/message/message.html | 20 +++++++ .../announcements.html} | 15 ++--- ietf/urls.py | 2 +- 14 files changed, 125 insertions(+), 102 deletions(-) delete mode 100644 ietf/announcements/models.py delete mode 100644 ietf/announcements/urls.py delete mode 100644 ietf/announcements/views.py create mode 100644 ietf/message/views.py create mode 100644 ietf/nomcom/redirect_ann_urls.py delete mode 100644 ietf/templates/announcements/message_detail.html create mode 100644 ietf/templates/message/message.html rename ietf/templates/{announcements/nomcomREDESIGN.html => nomcom/announcements.html} (91%) diff --git a/ietf/announcements/models.py b/ietf/announcements/models.py deleted file mode 100644 index 137941ffa..000000000 --- a/ietf/announcements/models.py +++ /dev/null @@ -1 +0,0 @@ -from django.db import models diff --git a/ietf/announcements/urls.py b/ietf/announcements/urls.py deleted file mode 100644 index f988e1785..000000000 --- a/ietf/announcements/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright The IETF Trust 2007, All Rights Reserved - -from django.conf.urls.defaults import patterns - -from django.conf import settings - -urlpatterns = patterns('', - (r'^nomcom/$', 'ietf.announcements.views.nomcom'), - (r'^nomcom/(?P\d+)/$', 'ietf.announcements.views.message_detail'), -) diff --git a/ietf/announcements/views.py b/ietf/announcements/views.py deleted file mode 100644 index 166ec2e8d..000000000 --- a/ietf/announcements/views.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright The IETF Trust 2007, All Rights Reserved - -import re - -from django.views.generic.simple import direct_to_template -from django.shortcuts import get_object_or_404 -from django.conf import settings -from django.db.models import Q - -from ietf.idtracker.models import ChairsHistory -from ietf.idtracker.models import Role -from ietf.group.models import Group, GroupEvent -from ietf.message.models import Message - -def nomcom(request): - address_re = re.compile("<.*>") - - nomcoms = list(Group.objects.filter(acronym__startswith="nomcom").exclude(name="nomcom")) - - regimes = [] - - for n in nomcoms: - e = GroupEvent.objects.filter(group=n, type="changed_state", changestategroupevent__state="active").order_by('time')[:1] - n.start_year = e[0].time.year if e else 0 - if n.start_year <= 2003: - continue - e = GroupEvent.objects.filter(group=n, type="changed_state", changestategroupevent__state="conclude").order_by('time')[:1] - n.end_year = e[0].time.year if e else "" - - r = n.role_set.select_related().filter(name="chair") - chair = None - if r: - chair = r[0] - announcements = Message.objects.filter(related_groups=n).order_by('-time') - for a in announcements: - a.to_name = address_re.sub("", a.to) - - regimes.append(dict(chair=chair, - announcements=announcements, - group=n)) - - regimes.sort(key=lambda x: x["group"].start_year, reverse=True) - - return direct_to_template(request, - "announcements/nomcomREDESIGN.html", - { 'curr_chair' : regimes[0]["chair"], - 'regimes' : regimes }) - -def message_detail(request, object_id): - # restrict to nomcom announcements for the time being - nomcoms = Group.objects.filter(acronym__startswith="nomcom").exclude(acronym="nomcom") - m = get_object_or_404(Message, id=object_id, - related_groups__in=nomcoms) - - return direct_to_template(request, - "announcements/message_detail.html", - dict(message=m)) - - diff --git a/ietf/message/views.py b/ietf/message/views.py new file mode 100644 index 000000000..b701f7fd6 --- /dev/null +++ b/ietf/message/views.py @@ -0,0 +1,15 @@ +from django.shortcuts import render_to_response, get_object_or_404 +from django.template import RequestContext + +from ietf.group.models import Group +from ietf.message.models import Message + + +def message(request, message_id, group_type): + possible_messages = Message.objects.filter(related_groups__type=group_type) + + message = get_object_or_404(possible_messages, id=message_id) + + return render_to_response("message/message.html", + dict(message=message), + context_instance=RequestContext(request)) diff --git a/ietf/nomcom/redirect_ann_urls.py b/ietf/nomcom/redirect_ann_urls.py new file mode 100644 index 000000000..993680f78 --- /dev/null +++ b/ietf/nomcom/redirect_ann_urls.py @@ -0,0 +1,7 @@ +from django.conf.urls.defaults import patterns +from django.views.generic.simple import redirect_to + +urlpatterns = patterns('', + (r'^nomcom/$', 'django.views.generic.simple.redirect_to', { 'url': "/nomcom/ann/", 'permanent': True }), + (r'^nomcom/(?P\d+)/$', 'django.views.generic.simple.redirect_to', { 'url': "/nomcom/ann/%(message_id)s/", 'permanent': True }), +) diff --git a/ietf/nomcom/test_data.py b/ietf/nomcom/test_data.py index bd20b8b94..96f31b739 100644 --- a/ietf/nomcom/test_data.py +++ b/ietf/nomcom/test_data.py @@ -6,7 +6,7 @@ from django.core.files import File from django.conf import settings from ietf.utils.pipe import pipe -from ietf.group.models import Group, Role +from ietf.group.models import Group, Role, ChangeStateGroupEvent from ietf.person.models import Email, Person from ietf.name.models import RoleName from ietf.nomcom.models import NomCom, Position, Nominee @@ -101,6 +101,7 @@ def nomcom_test_data(): state_id='active', type_id='nomcom', acronym='nomcom%s' % NOMCOM_YEAR) + nomcom, created = NomCom.objects.get_or_create(group=group) cert_file, privatekey_file = generate_cert() @@ -159,3 +160,9 @@ def nomcom_test_data(): description=description, is_open=True, incumbent=email) + + ChangeStateGroupEvent.objects.get_or_create(group=group, + type="changed_state", + state_id="active", + time=group.time, + by=Person.objects.all()[0]) diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 864ba0ae8..3d8acc65d 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -8,13 +8,15 @@ from django.db import IntegrityError from django.core.urlresolvers import reverse from django.core.files import File from django.contrib.formtools.preview import security_hash +from django.contrib.auth.models import User from ietf.utils.test_utils import login_testing_unauthorized from ietf.utils.mail import outbox from ietf.person.models import Email, Person -from django.contrib.auth.models import User +from ietf.group.models import Group +from ietf.message.models import Message from ietf.nomcom.test_data import nomcom_test_data, generate_cert, check_comments, \ COMMUNITY_USER, CHAIR_USER, \ @@ -365,6 +367,25 @@ class NomcomViewsTest(TestCase): """Verify home view""" self.check_url_status(self.index_url, 200) + def test_announcements_view(self): + nomcom = Group.objects.get(acronym="nomcom%s" % self.year, type="nomcom") + msg = Message.objects.create( + by=Person.objects.all()[0], + subject="This is a test", + to="test@example.com", + frm="nomcomchair@example.com", + body="Hello World!", + content_type="", + ) + msg.related_groups.add(nomcom) + + r = self.client.get(reverse('ietf.nomcom.views.announcements')) + self.assertEqual(r.status_code, 200) + self.assertTrue(("Messages from %s" % nomcom.time.year) in r.content) + self.assertTrue(nomcom.role_set.filter(name="chair")[0].person.email_address() in r.content) + self.assertTrue(msg.subject in r.content) + + def test_requirements_view(self): """Verify requirements view""" self.check_url_status(self.requirements_url, 200) diff --git a/ietf/nomcom/urls.py b/ietf/nomcom/urls.py index 4bfdbfca0..595cdcbc3 100644 --- a/ietf/nomcom/urls.py +++ b/ietf/nomcom/urls.py @@ -6,6 +6,7 @@ from ietf.nomcom.forms import EditChairForm, EditChairFormPreview, \ urlpatterns = patterns('ietf.nomcom.views', url(r'^$', 'index'), + url(r'^ann/$', 'announcements'), url(r'^(?P\d{4})/private/$', 'private_index', name='nomcom_private_index'), url(r'^(?P\d{4})/private/key/$', 'private_key', name='nomcom_private_key'), url(r'^(?P\d{4})/private/nominate/$', 'private_nominate', name='nomcom_private_nominate'), @@ -42,3 +43,7 @@ urlpatterns = patterns('ietf.nomcom.views', url(r'^ajax/position-text/(?P\d+)/$', 'ajax_position_text', name='nomcom_ajax_position_text'), ) + +urlpatterns += patterns('', + url(r'^ann/(?P\d+)/$', 'ietf.message.views.message', {'group_type': "nomcom" }, "nomcom_announcement"), +) diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index d6632c3ad..c69e6f0ac 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import datetime +import datetime, re from django.views.generic.create_update import delete_object from django.conf import settings @@ -20,7 +20,8 @@ from django.forms.models import modelformset_factory, inlineformset_factory from ietf.dbtemplate.models import DBTemplate from ietf.dbtemplate.views import template_edit from ietf.name.models import NomineePositionState, FeedbackType -from ietf.group.models import Group +from ietf.group.models import Group, GroupEvent +from ietf.message.models import Message from ietf.nomcom.decorators import nomcom_private_key_required from ietf.nomcom.forms import (NominateForm, FeedbackForm, QuestionnaireForm, @@ -53,7 +54,7 @@ def index(request): else: nomcom.url = None if year >= 2002: - nomcom.ann_url = "/ann/nomcom/#%4d" % year + nomcom.ann_url = "/nomcom/ann/#%4d" % year else: nomcom.ann_url = None return render_to_response('nomcom/index.html', @@ -70,6 +71,41 @@ def year_index(request, year): 'selected': 'index', 'template': template}, RequestContext(request)) +def announcements(request): + address_re = re.compile("<.*>") + + nomcoms = Group.objects.filter(type="nomcom") + + regimes = [] + + for n in nomcoms: + e = GroupEvent.objects.filter(group=n, type="changed_state", changestategroupevent__state="active").order_by('time')[:1] + n.start_year = e[0].time.year if e else 0 + e = GroupEvent.objects.filter(group=n, type="changed_state", changestategroupevent__state="conclude").order_by('time')[:1] + n.end_year = e[0].time.year if e else n.start_year + 1 + + r = n.role_set.select_related().filter(name="chair") + chair = None + if r: + chair = r[0] + + announcements = Message.objects.filter(related_groups=n).order_by('-time') + for a in announcements: + a.to_name = address_re.sub("", a.to) + + if not announcements: + continue + + regimes.append(dict(chair=chair, + announcements=announcements, + group=n)) + + regimes.sort(key=lambda x: x["group"].start_year, reverse=True) + + return render_to_response("nomcom/announcements.html", + { 'curr_chair' : regimes[0]["chair"] if regimes else None, + 'regimes' : regimes }, + context_instance=RequestContext(request)) @role_required("Nomcom") def private_key(request, year): diff --git a/ietf/settings.py b/ietf/settings.py index 42c00c66b..887fc9246 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -166,7 +166,6 @@ INSTALLED_APPS = ( 'ietf.group', 'ietf.doc', 'ietf.message', - 'ietf.announcements', 'ietf.idindex', 'ietf.idtracker', 'ietf.ietfauth', diff --git a/ietf/templates/announcements/message_detail.html b/ietf/templates/announcements/message_detail.html deleted file mode 100644 index 29dbbb4be..000000000 --- a/ietf/templates/announcements/message_detail.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} -{% load ietf_filters %} - -{% block title %}Announcement: {{ message.time|date:"F j, Y" }} -- {{ message.subject|escape }}{% endblock %} - -{% block content %} -

      NomCom Message

      -

      -From: {{ message.frm|escape }}
      -To: {{ message.to|escape }}
      -Date: {{ message.time|date:"F j, Y" }}
      -Subject: {{ message.subject|escape }} -

      -
      -
      -{{ message.body|escape }}
      -
      -{% endblock %} diff --git a/ietf/templates/message/message.html b/ietf/templates/message/message.html new file mode 100644 index 000000000..dc8a62059 --- /dev/null +++ b/ietf/templates/message/message.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% load ietf_filters %} + +{% block title %}Announcement: {{ message.time|date:"F j, Y" }} - {{ message.subject }}{% endblock %} + +{% block content %} +

      Announcement: {{ message.time|date:"F j, Y" }} - {{ message.subject }}

      + +

      + From: {{ message.frm }}
      + To: {{ message.to }}
      + Date: {{ message.time|date:"F j, Y" }}
      + Subject: {{ message.subject }} +

      + +
      +
      +{{ message.body }}
      +
      +{% endblock %} diff --git a/ietf/templates/announcements/nomcomREDESIGN.html b/ietf/templates/nomcom/announcements.html similarity index 91% rename from ietf/templates/announcements/nomcomREDESIGN.html rename to ietf/templates/nomcom/announcements.html index c312c99cc..8e973dedf 100644 --- a/ietf/templates/announcements/nomcomREDESIGN.html +++ b/ietf/templates/nomcom/announcements.html @@ -8,8 +8,9 @@

      Current Committee Chair: {{ curr_chair.person.plain_name }}

      {% for regime in regimes %} -
      -

      Messages from {{ regime.group.start_year }} - {{ regime.group.end_year }}

      +
      + +

      Messages from {{ regime.group.start_year }} - {{ regime.group.end_year }}

      {# use person email address here rather than the generic nomcom-chair@ietf.org #}

      Committee Chair: {{ regime.chair.person.plain_name }}

      @@ -19,17 +20,17 @@ Subject Sent To - {% for ann in regime.announcements %} + {% for m in regime.announcements %} - {{ ann.time|date:"Y-M-d" }} - {{ ann.subject|escape }} - {{ ann.to_name }} + {{ m.time|date:"Y-M-d" }} + {{ m.subject|escape }} + {{ m.to_name }} {% endfor %} {% endfor %} -
      +
      {# somebody ought to import these announcements in the DB instead of this mess #} diff --git a/ietf/urls.py b/ietf/urls.py index fc14c83d7..41c4cfd5f 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -50,7 +50,7 @@ urlpatterns = patterns('', (r'^accounts/', include('ietf.ietfauth.urls')), (r'^admin/', include(admin.site.urls)), (r'^admin/doc/', include('django.contrib.admindocs.urls')), - (r'^ann/', include('ietf.announcements.urls')), + (r'^ann/', include('ietf.nomcom.redirect_ann_urls')), (r'^community/', include('ietf.community.urls')), (r'^cookies/', include('ietf.cookies.urls')), (r'^doc/', include('ietf.doc.urls')), From 85d998a18a051da07e78307087bced3c4b77c0d3 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 28 Nov 2013 17:58:06 +0000 Subject: [PATCH 121/173] Remove the old NomCom announcements sitemap, with the existing overview page it does not appear to be useful at all, remove remaining traces of the old announcements app - Legacy-Id: 6770 --- ietf/announcements/.gitignore | 2 - ietf/announcements/__init__.py | 2 - ietf/announcements/sitemaps.py | 15 --- ietf/announcements/tests.py | 20 ---- ietf/announcements/testurl.list | 5 - ietf/message/tests.py | 94 +++++++++++++++++++ ietf/templates/announcements/.gitignore | 1 - .../announcements/announcement_detail.html | 19 ---- ietf/templates/announcements/nomcom.html | 74 --------------- ietf/urls.py | 2 - 10 files changed, 94 insertions(+), 140 deletions(-) delete mode 100644 ietf/announcements/.gitignore delete mode 100644 ietf/announcements/__init__.py delete mode 100644 ietf/announcements/sitemaps.py delete mode 100644 ietf/announcements/tests.py delete mode 100644 ietf/announcements/testurl.list create mode 100644 ietf/message/tests.py delete mode 100644 ietf/templates/announcements/.gitignore delete mode 100644 ietf/templates/announcements/announcement_detail.html delete mode 100644 ietf/templates/announcements/nomcom.html diff --git a/ietf/announcements/.gitignore b/ietf/announcements/.gitignore deleted file mode 100644 index c7013ced9..000000000 --- a/ietf/announcements/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.pyc -/settings_local.py diff --git a/ietf/announcements/__init__.py b/ietf/announcements/__init__.py deleted file mode 100644 index a4b306690..000000000 --- a/ietf/announcements/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright The IETF Trust 2007, All Rights Reserved - diff --git a/ietf/announcements/sitemaps.py b/ietf/announcements/sitemaps.py deleted file mode 100644 index 9cf648cf7..000000000 --- a/ietf/announcements/sitemaps.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The IETF Trust 2007, All Rights Reserved - -from django.conf import settings -from django.contrib.sitemaps import Sitemap -from ietf.message.models import Message - -class NOMCOMAnnouncementsMap(Sitemap): - changefreq = "never" - def items(self): - return Message.objects.filter(related_groups__acronym__startswith="nomcom").exclude(related_groups__acronym="nomcom").order_by('-time') - def location(self, obj): - return "/ann/nomcom/%d/" % obj.id - def lastmod(self, obj): - # could re-parse the time into a datetime object - return obj.time diff --git a/ietf/announcements/tests.py b/ietf/announcements/tests.py deleted file mode 100644 index e6ec9226a..000000000 --- a/ietf/announcements/tests.py +++ /dev/null @@ -1,20 +0,0 @@ -import datetime - -from django.conf import settings - -from ietf.utils.test_utils import SimpleUrlTestCase, canonicalize_sitemap -from ietf.utils.test_data import make_test_data -from ietf.utils.mail import outbox -from ietf.utils import TestCase - -from ietf.message.models import Message, SendQueue -from ietf.person.models import Person - -class AnnouncementsUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) - def doCanonicalize(self, url, content): - if url.startswith("/sitemap"): - return canonicalize_sitemap(content) - else: - return content diff --git a/ietf/announcements/testurl.list b/ietf/announcements/testurl.list deleted file mode 100644 index 5c88659c4..000000000 --- a/ietf/announcements/testurl.list +++ /dev/null @@ -1,5 +0,0 @@ -200 /ann/nomcom/ -200 /ann/nomcom/1230/ -200 /ann/nomcom/1607/ # non-ASCII content -200 /ann/nomcom/2001/ # non-ASCII content -200 /sitemap-nomcom-announcements.xml diff --git a/ietf/message/tests.py b/ietf/message/tests.py new file mode 100644 index 000000000..dbb964da7 --- /dev/null +++ b/ietf/message/tests.py @@ -0,0 +1,94 @@ +import datetime + +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse + +from ietf.utils.test_utils import TestCase +from ietf.utils.test_data import make_test_data +from ietf.utils.mail import outbox + +from ietf.message.models import Message, SendQueue +from ietf.person.models import Person +from ietf.group.models import Group +from ietf.message.utils import send_scheduled_message_from_send_queue + +class MessageTests(TestCase): + def test_message_view(self): + make_test_data() + + nomcom = Group.objects.create(name="nomcom%s" % datetime.date.today().year, type_id="nomcom") + msg = Message.objects.create( + by=Person.objects.get(name="(System)"), + subject="This is a test", + to="test@example.com", + frm="nomcomchair@example.com", + body="Hello World!", + content_type="", + ) + msg.related_groups.add(nomcom) + + r = self.client.get(urlreverse("nomcom_announcement", kwargs=dict(message_id=msg.id))) + self.assertEquals(r.status_code, 200) + self.assertTrue(msg.subject in r.content) + self.assertTrue(msg.to in r.content) + self.assertTrue(msg.frm in r.content) + self.assertTrue("Hello World!" in r.content) + + +class SendScheduledAnnouncementsTests(TestCase): + def test_send_plain_announcement(self): + make_test_data() + + msg = Message.objects.create( + by=Person.objects.get(name="(System)"), + subject="This is a test", + to="test@example.com", + frm="testmonkey@example.com", + cc="cc.a@example.com, cc.b@example.com", + bcc="bcc@example.com", + body="Hello World!", + content_type="", + ) + + q = SendQueue.objects.create( + by=Person.objects.get(name="(System)"), + message=msg, + send_at=datetime.datetime.now() + datetime.timedelta(hours=12) + ) + + mailbox_before = len(outbox) + + send_scheduled_message_from_send_queue(q) + + self.assertEquals(len(outbox), mailbox_before + 1) + self.assertTrue("This is a test" in outbox[-1]["Subject"]) + self.assertTrue(SendQueue.objects.get(id=q.id).sent_at) + + def test_send_mime_announcement(self): + make_test_data() + + msg = Message.objects.create( + by=Person.objects.get(name="(System)"), + subject="This is a test", + to="test@example.com", + frm="testmonkey@example.com", + cc="cc.a@example.com, cc.b@example.com", + bcc="bcc@example.com", + body='--NextPart\r\n\r\nA New Internet-Draft is available from the on-line Internet-Drafts directories.\r\n--NextPart\r\nContent-Type: Message/External-body;\r\n\tname="draft-huang-behave-bih-01.txt";\r\n\tsite="ftp.ietf.org";\r\n\taccess-type="anon-ftp";\r\n\tdirectory="internet-drafts"\r\n\r\nContent-Type: text/plain\r\nContent-ID: <2010-07-30001541.I-D@ietf.org>\r\n\r\n--NextPart--', + content_type='Multipart/Mixed; Boundary="NextPart"', + ) + + q = SendQueue.objects.create( + by=Person.objects.get(name="(System)"), + message=msg, + send_at=datetime.datetime.now() + datetime.timedelta(hours=12) + ) + + mailbox_before = len(outbox) + + send_scheduled_message_from_send_queue(q) + + self.assertEquals(len(outbox), mailbox_before + 1) + self.assertTrue("This is a test" in outbox[-1]["Subject"]) + self.assertTrue("--NextPart" in outbox[-1].as_string()) + self.assertTrue(SendQueue.objects.get(id=q.id).sent_at) diff --git a/ietf/templates/announcements/.gitignore b/ietf/templates/announcements/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/templates/announcements/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/templates/announcements/announcement_detail.html b/ietf/templates/announcements/announcement_detail.html deleted file mode 100644 index 5417135ce..000000000 --- a/ietf/templates/announcements/announcement_detail.html +++ /dev/null @@ -1,19 +0,0 @@ -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% extends "base.html" %} -{% load ietf_filters %} - -{% block title %}NomCom Message: {{ object.announced_date|date:"F j, Y" }} -- {{ object.subject|escape }}{% endblock %} - -{% block content %} -

      NomCom Message

      -

      -From: {{ object.from_name|escape }}
      -To: {{ object.announced_to|escape }}
      -Date: {{ object.announced_date|date:"F j, Y" }}
      -Subject: {{ object.subject|escape }} -

      -
      -
      -{{ object.text|escape }}
      -
      -{% endblock %} diff --git a/ietf/templates/announcements/nomcom.html b/ietf/templates/announcements/nomcom.html deleted file mode 100644 index c5966b30e..000000000 --- a/ietf/templates/announcements/nomcom.html +++ /dev/null @@ -1,74 +0,0 @@ -{% extends "base.html" %} -{% load ietf_filters %} -{% block title %}IAB/IESG Nominating Committee{% endblock %} -{% block content %} - -

      IAB/IESG Nominating Committee

      - -

      Current Committee Chair: {{ curr_chair.person.email.0 }}

      - -{% for regime in regimes %} -
      -

      Messages from {{ regime.chair.start_year }} - {{ regime.chair.end_year }}

      -

      Committee Chair: {{ regime.chair.person }}

      - - - - - - - {% for ann in regime.announcements %} - - - - - - {% endfor %} -
      DateSubjectSent To
      {{ ann.announced_date|date:"Y-M-d" }}{{ ann.subject|escape }}{{ ann.announced_to }}
      -{% endfor %} - -
      - -

      Messages from 2003-2004 NomCom

      -Committee Chair: Rich Draves -

    • IETF Nominations Committee Chair Announcement August 25, 2003 -
    • NomCom call for volunteers September 22, 2003 -
    • Selection of the Nominations Committee -
    • NomCom Volunteer List October 06, 2004 -
    • NomCom Selection October 10, 2003 -
    • Call for Nominees October 17, 2003 -
    • NomCom members October 24, 2003 -
    • NomCom at IETF November 07, 2003 -
    • NomCom News November 14, 2003 -
    • Reminder - nominations to replace Randy Bush November 26, 2003 -
    • Randy Bush replacement schedule December 01, 2003 -
    • Randy Bush replacement January 14, 2004 -
    • NomCom results February 13, 2004 -
    • Call for Security AD nominations September 28, 2004 -
    • Steve Bellovin replacement November 07, 2004 - -

      Messages from 2002-2003 NomCom

      - -Committee Chair: Phil Roberts -

      -
    • First Call for Volunteers July 30, 2002 -
    • Selection of the Nominations Committee -
    • Announcement of the Nominations Committee September 18, 2002 -
    • Announcement of IESG and IAB Nominations Requests October 21, 2002 -
    • Announcement of IESG and IAB Nominations Requests November 5, 2002 -
    • Announcement of IESG and IAB Nominations Requests November 12, 2002 -
    • IETF Nomcom Announcement February 27, 2003 -
    • Announcement of IESG and IAB Nominations Request June 11, 2003 -
    • Nomcom result announcement July 15, 2003 - -

      Historical Information

      - -
    • IAB/IESG Nominating Committee Members (by year) - -

      References

      - -
    • The Internet Standards Process (RFC 2026) -
    • IAB and IESG Selection, Confirmation, and Recall Process: Operation of the Nominating and Recall Committees (RFC 3777) (Also BCP10) -
    • Publicly Verifiable Nominations Committee (NomCom) Random Selection (RFC 3797) - -{% endblock %} diff --git a/ietf/urls.py b/ietf/urls.py index 41c4cfd5f..bf4012abd 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -13,7 +13,6 @@ from ietf.wgcharter.feeds import GroupChanges from ietf.liaisons.sitemaps import LiaisonMap from ietf.ipr.sitemaps import IPRMap -from ietf.announcements.sitemaps import NOMCOMAnnouncementsMap from django.conf import settings @@ -42,7 +41,6 @@ feeds = { sitemaps = { 'liaison': LiaisonMap, 'ipr': IPRMap, - 'nomcom-announcements': NOMCOMAnnouncementsMap, } urlpatterns = patterns('', From 6c2b3be8d613b0d8e82f7ce75a703cc5573b6ee5 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 28 Nov 2013 18:08:30 +0000 Subject: [PATCH 122/173] Fix admin for SendQueue so message is in raw_id_fields - Legacy-Id: 6771 --- ietf/message/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/message/admin.py b/ietf/message/admin.py index d7e242067..5ba741f37 100644 --- a/ietf/message/admin.py +++ b/ietf/message/admin.py @@ -17,7 +17,7 @@ class SendQueueAdmin(admin.ModelAdmin): list_display = ["time", "by", "message", "send_at", "sent_at"] list_filter = ["time", "send_at", "sent_at"] search_fields = ["message__body"] - raw_id_fields = ["by"] + raw_id_fields = ["by", "message"] ordering = ["-time"] admin.site.register(SendQueue, SendQueueAdmin) From 6d94351553b12369c74ed3e030d7463636b28d38 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 28 Nov 2013 18:12:45 +0000 Subject: [PATCH 123/173] Add __unicode__ to SendQueue - Legacy-Id: 6772 --- ietf/message/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ietf/message/models.py b/ietf/message/models.py index 91b2f44f2..fcecac73e 100644 --- a/ietf/message/models.py +++ b/ietf/message/models.py @@ -41,3 +41,6 @@ class SendQueue(models.Model): class Meta: ordering = ['time'] + + def __unicode__(self): + return u"'%s' %s -> %s (sent at %s)" % (self.message.subject, self.message.frm, self.message.to, self.sent_at or "") From f7bed5d0aae343d1288792c366a1b5f56c922614 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 29 Nov 2013 11:48:47 +0000 Subject: [PATCH 124/173] Remove Django 0.x support from ipr/feeds.py - Legacy-Id: 6773 --- ietf/ipr/feeds.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ietf/ipr/feeds.py b/ietf/ipr/feeds.py index 4a1900302..b92a98a99 100644 --- a/ietf/ipr/feeds.py +++ b/ietf/ipr/feeds.py @@ -24,12 +24,7 @@ class LatestIprDisclosures(Feed): def item_author_name(self, item): s = item.get_submitter() if s: - if isinstance(s.name, unicode): - # for django.VERSION[0] > 0 - return s.name - else: - # for django.VERSION[0] == 0 - return unicode(s.name, encoding='utf-8', errors='replace') + return s.name return None def item_author_email(self, item): s = item.get_submitter() From e9638cb1c2b380cfb99b7fcc2cf5aac4a62572c8 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 2 Dec 2013 16:34:00 +0000 Subject: [PATCH 125/173] Port IPR module to new schema, remove some cruft - Legacy-Id: 6774 --- ietf/doc/proxy.py | 46 +++++- ietf/ipr/__init__.py | 1 - ietf/ipr/admin.py | 16 +- ietf/ipr/feeds.py | 2 +- ietf/ipr/models.py | 72 +------- ietf/ipr/new.py | 104 ++++-------- ietf/ipr/search.py | 32 +--- ietf/ipr/urls.py | 4 +- ietf/ipr/view_sections.py | 13 +- ietf/ipr/views.py | 104 ++++-------- ietf/secr/ipradmin/forms.py | 11 +- ietf/secr/ipradmin/models.py | 192 ---------------------- ietf/secr/ipradmin/views.py | 10 +- ietf/templates/ipr/details.html | 8 +- ietf/templates/ipr/drafts.html | 3 - ietf/templates/ipr/list.html | 2 +- ietf/templates/ipr/list_item.html | 12 +- ietf/templates/ipr/removed.html | 4 +- ietf/templates/ipr/search.html | 20 +-- ietf/templates/ipr/search_doc_result.html | 6 +- ietf/templates/ipr/search_result.html | 22 +-- ietf/templates/ipr/search_wg_result.html | 4 +- 22 files changed, 192 insertions(+), 496 deletions(-) delete mode 100644 ietf/templates/ipr/drafts.html diff --git a/ietf/doc/proxy.py b/ietf/doc/proxy.py index 6b2fbe15c..c302bd268 100644 --- a/ietf/doc/proxy.py +++ b/ietf/doc/proxy.py @@ -714,7 +714,6 @@ class InternetDraft(Document): @property def ipr(self): - from ietf.ipr.models import IprDraftProxy return IprDraftProxy.objects.filter(doc_alias__document=self.pk) class Meta: @@ -888,7 +887,6 @@ class DraftLikeDocAlias(DocAlias): @property def ipr(self): - from ietf.ipr.models import IprDraftProxy return IprDraftProxy.objects.filter(doc_alias=self.pk) class Meta: @@ -996,3 +994,47 @@ class IDState(State): proxy = True + + +# proxy stuff for ipr +from ietf.ipr.models import IprDocAlias + +class IprDraftProxy(IprDocAlias): + objects = TranslatingManager(dict(document="doc_alias__name")) + + # document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr") + # document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr") + @property + def document(self): + from ietf.doc.proxy import DraftLikeDocAlias + return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id) + + #revision = models.CharField(max_length=2) + @property + def revision(self): + return self.rev + + class Meta: + proxy = True + +IprDraft = IprDraftProxy + +class IprRfcProxy(IprDocAlias): + objects = TranslatingManager(dict(document=lambda v: ("doc_alias__name", "rfc%s" % v))) + + # document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr") + # document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr") + @property + def document(self): + from ietf.doc.proxy import DraftLikeDocAlias + return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id) + + #revision = models.CharField(max_length=2) + @property + def revision(self): + return self.rev + + class Meta: + proxy = True + +IprRfc = IprRfcProxy diff --git a/ietf/ipr/__init__.py b/ietf/ipr/__init__.py index a4b306690..8b1378917 100644 --- a/ietf/ipr/__init__.py +++ b/ietf/ipr/__init__.py @@ -1,2 +1 @@ -# Copyright The IETF Trust 2007, All Rights Reserved diff --git a/ietf/ipr/admin.py b/ietf/ipr/admin.py index ca96d2b09..8cc9c81e3 100644 --- a/ietf/ipr/admin.py +++ b/ietf/ipr/admin.py @@ -8,8 +8,20 @@ class IprContactAdmin(admin.ModelAdmin): admin.site.register(IprContact, IprContactAdmin) class IprDetailAdmin(admin.ModelAdmin): - list_display = ['title', 'submitted_date', 'docs', ] - search_fields = ['title', 'legal_name', ] + list_display = ['title', 'submitted_date', 'docs', 'status'] + search_fields = ['title', 'legal_name'] + + def docs(self, ipr): + res = [] + for iprdocalias in IprDocAlias.objects.filter(ipr=ipr).order_by("id").select_related("doc_alias"): + if iprdocalias.doc_alias.name.startswith("rfc"): + n = iprdocalias.doc_alias.name.upper() + elif iprdocalias.rev: + n = iprdocalias.doc_alias.name + iprdocalias.rev + else: + n = iprdocalias.doc_alias.name + res.append(n) + return u", ".join(res) admin.site.register(IprDetail, IprDetailAdmin) class IprLicensingAdmin(admin.ModelAdmin): diff --git a/ietf/ipr/feeds.py b/ietf/ipr/feeds.py index b92a98a99..3afac4b13 100644 --- a/ietf/ipr/feeds.py +++ b/ietf/ipr/feeds.py @@ -22,7 +22,7 @@ class LatestIprDisclosures(Feed): # though the database has only date, not time return datetime.combine(item.submitted_date, time(0,0,0)) def item_author_name(self, item): - s = item.get_submitter() + s = item.get_submitter() if s: return s.name return None diff --git a/ietf/ipr/models.py b/ietf/ipr/models.py index 9b84a3a24..30e0c6941 100644 --- a/ietf/ipr/models.py +++ b/ietf/ipr/models.py @@ -1,11 +1,9 @@ # Copyright The IETF Trust 2007, All Rights Reserved from django.db import models -from django.conf import settings -from ietf.utils.lazy import reverse_lazy -# ------------------------------------------------------------------------ -# Models +from ietf.doc.models import DocAlias + LICENSE_CHOICES = ( (1, 'a) No License Required for Implementers.'), @@ -42,20 +40,14 @@ class IprSelecttype(models.Model): type_id = models.AutoField(primary_key=True) is_pending = models.IntegerField(unique=True, db_column="selecttype") type_display = models.CharField(blank=True, max_length=15) - def __str__(self): + def __unicode__(self): return self.type_display - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'ipr_selecttype' class IprLicensing(models.Model): licensing_option = models.AutoField(primary_key=True) value = models.CharField(max_length=255, db_column='licensing_option_value') - def __str__(self): + def __unicode__(self): return self.value; - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'ipr_licensing' class IprDetail(models.Model): @@ -115,17 +107,8 @@ class IprDetail(models.Model): submitted_date = models.DateField(blank=True) update_notified_date = models.DateField(null=True, blank=True) - def __str__(self): - return self.title def __unicode__(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # the latin-1 decode doesn't seem necessary anymore - return self.title - return self.title.decode("latin-1", 'replace') - def docs(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return list(IprDraftProxy.objects.filter(ipr=self)) - return list(self.drafts.all()) + list(self.rfcs.all()) + return self.title @models.permalink def get_absolute_url(self): return ('ietf.ipr.views.show', [str(self.ipr_id)]) @@ -173,8 +156,6 @@ class IprUpdate(models.Model): processed = models.IntegerField(null=True, blank=True) -from ietf.doc.models import DocAlias - class IprDocAlias(models.Model): ipr = models.ForeignKey(IprDetail, related_name='documents') doc_alias = models.ForeignKey(DocAlias) @@ -189,46 +170,3 @@ class IprDocAlias(models.Model): verbose_name = "IPR document alias" verbose_name_plural = "IPR document aliases" -# proxy stuff - -from ietf.utils.proxy import TranslatingManager - -class IprDraftProxy(IprDocAlias): - objects = TranslatingManager(dict(document="doc_alias__name")) - - # document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr") - # document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr") - @property - def document(self): - from ietf.doc.proxy import DraftLikeDocAlias - return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id) - - #revision = models.CharField(max_length=2) - @property - def revision(self): - return self.rev - - class Meta: - proxy = True - -IprDraft = IprDraftProxy - -class IprRfcProxy(IprDocAlias): - objects = TranslatingManager(dict(document=lambda v: ("doc_alias__name", "rfc%s" % v))) - - # document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr") - # document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr") - @property - def document(self): - from ietf.doc.proxy import DraftLikeDocAlias - return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id) - - #revision = models.CharField(max_length=2) - @property - def revision(self): - return self.rev - - class Meta: - proxy = True - -IprRfc = IprRfcProxy diff --git a/ietf/ipr/new.py b/ietf/ipr/new.py index 0aae2ed91..a1524898b 100644 --- a/ietf/ipr/new.py +++ b/ietf/ipr/new.py @@ -1,19 +1,17 @@ # Copyright The IETF Trust 2007, All Rights Reserved import re -import models -import ietf.utils -from django import forms -from datetime import datetime from django.shortcuts import render_to_response as render, get_object_or_404 from django.template import RequestContext from django.http import Http404 from django.conf import settings +from django import forms + from ietf.utils import log from ietf.utils.mail import send_mail +from ietf.ipr.models import IprDetail, IprDocAlias, IprContact, LICENSE_CHOICES, IprUpdate from ietf.ipr.view_sections import section_table -from ietf.idtracker.models import Rfc, InternetDraft # ---------------------------------------------------------------- # Create base forms from models @@ -22,21 +20,19 @@ from ietf.idtracker.models import Rfc, InternetDraft phone_re = re.compile(r'^\+?[0-9 ]*(\([0-9]+\))?[0-9 -]+( ?x ?[0-9]+)?$') phone_error_message = """Phone numbers may have a leading "+", and otherwise only contain numbers [0-9]; dash, period or space; parentheses, and an optional extension number indicated by 'x'.""" -from django.forms import ModelForm - -class BaseIprForm(ModelForm): - licensing_option = forms.IntegerField(widget=forms.RadioSelect(choices=models.LICENSE_CHOICES), required=False) +class BaseIprForm(forms.ModelForm): + licensing_option = forms.IntegerField(widget=forms.RadioSelect(choices=LICENSE_CHOICES), required=False) is_pending = forms.IntegerField(widget=forms.RadioSelect(choices=((1, "YES"), (2, "NO"))), required=False) applies_to_all = forms.IntegerField(widget=forms.RadioSelect(choices=((1, "YES"), (2, "NO"))), required=False) class Meta: - model = models.IprDetail + model = IprDetail exclude = ('rfc_document', 'id_document_tag') # 'legacy_url_0','legacy_url_1','legacy_title_1','legacy_url_2','legacy_title_2') -class BaseContactForm(ModelForm): +class BaseContactForm(forms.ModelForm): telephone = forms.RegexField(phone_re, error_message=phone_error_message) fax = forms.RegexField(phone_re, error_message=phone_error_message, required=False) class Meta: - model = models.IprContact + model = IprContact exclude = ('ipr', 'contact_type') # Some subclassing: @@ -102,19 +98,11 @@ def new(request, type, update=None, submitter=None): setattr(self, contact, ContactForm(prefix=contact[:4], initial=contact_initial.get(contact, {}), *args, **kwnoinit)) rfclist_initial = "" if update: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.ipr.models import IprDocAlias - rfclist_initial = " ".join(a.doc_alias.name.upper() for a in IprDocAlias.objects.filter(doc_alias__name__startswith="rfc", ipr=update)) - else: - rfclist_initial = " ".join(["RFC%d" % rfc.document_id for rfc in update.rfcs.all()]) + rfclist_initial = " ".join(a.doc_alias.name.upper() for a in IprDocAlias.objects.filter(doc_alias__name__startswith="rfc", ipr=update)) self.base_fields["rfclist"] = forms.CharField(required=False, initial=rfclist_initial) draftlist_initial = "" if update: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.ipr.models import IprDocAlias - draftlist_initial = " ".join(a.doc_alias.name + ("-%s" % a.rev if a.rev else "") for a in IprDocAlias.objects.filter(ipr=update).exclude(doc_alias__name__startswith="rfc")) - else: - draftlist_initial = " ".join([draft.document.filename + (draft.revision and "-%s" % draft.revision or "") for draft in update.drafts.all()]) + draftlist_initial = " ".join(a.doc_alias.name + ("-%s" % a.rev if a.rev else "") for a in IprDocAlias.objects.filter(ipr=update).exclude(doc_alias__name__startswith="rfc")) self.base_fields["draftlist"] = forms.CharField(required=False, initial=draftlist_initial) if section_list.get("holder_contact", False): self.base_fields["hold_contact_is_submitter"] = forms.BooleanField(required=False) @@ -142,11 +130,7 @@ def new(request, type, update=None, submitter=None): rfclist = rfclist.strip().split() for rfc in rfclist: try: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import DocAlias - DocAlias.objects.get(name="rfc%s" % int(rfc)) - else: - Rfc.objects.get(rfc_number=int(rfc)) + DocAlias.objects.get(name="rfc%s" % int(rfc)) except: raise forms.ValidationError("Unknown RFC number: %s - please correct this." % rfc) rfclist = " ".join(rfclist) @@ -167,19 +151,13 @@ def new(request, type, update=None, submitter=None): filename = draft rev = None try: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import DocAlias - id = DocAlias.objects.get(name=filename) - # proxy attribute for code below - id.revision = id.document.rev - else: - id = InternetDraft.objects.get(filename=filename) - except Exception, e: + doc = Document.objects.get(docalias__name=filename) + except Exception as e: log("Exception: %s" % e) raise forms.ValidationError("Unknown Internet-Draft: %s - please correct this." % filename) - if rev and id.revision != rev: + if rev and doc.rev != rev: raise forms.ValidationError("Unexpected revision '%s' for draft %s - the current revision is %s. Please check this." % (rev, filename, id.revision)) - drafts.append("%s-%s" % (filename, id.revision)) + drafts.append("%s-%s" % (filename, doc.rev)) return " ".join(drafts) return "" def clean_licensing_option(self): @@ -205,7 +183,7 @@ def new(request, type, update=None, submitter=None): data = request.POST.copy() else: data = request.GET.copy() - data["submitted_date"] = datetime.now().strftime("%Y-%m-%d") + data["submitted_date"] = datetime.datetime.now().strftime("%Y-%m-%d") data["third_party"] = section_list["third_party"] data["generic"] = section_list["generic"] data["status"] = "0" @@ -222,7 +200,7 @@ def new(request, type, update=None, submitter=None): form = IprForm(data) if form.is_valid(): # Save data : - # IprDetail, IprUpdate, IprContact+, IprDraft+, IprRfc+, IprNotification + # IprDetail, IprUpdate, IprContact+, IprDocAlias+, IprNotification # Save IprDetail instance = form.save(commit=False) @@ -248,7 +226,7 @@ def new(request, type, update=None, submitter=None): instance.save() if update: - updater = models.IprUpdate(ipr=instance, updated=update, status_to_be=1, processed=0) + updater = IprUpdate(ipr=instance, updated=update, status_to_be=1, processed=0) updater.save() contact_type = {"hold":1, "ietf":2, "subm": 3} @@ -263,7 +241,7 @@ def new(request, type, update=None, submitter=None): del cdata["contact_is_submitter"] except KeyError: pass - contact = models.IprContact(**dict([(str(a),b) for a,b in cdata.items()])) + contact = IprContact(**dict([(str(a),b) for a,b in cdata.items()])) contact.save() # store this contact in the instance for the email # similar to what views.show() does @@ -279,34 +257,21 @@ def new(request, type, update=None, submitter=None): # else: # log("Invalid contact: %s" % contact) - # Save IprDraft(s) + # Save draft links for draft in form.cleaned_data["draftlist"].split(): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - name = draft[:-3] - rev = draft[-2:] - - from ietf.doc.models import DocAlias - models.IprDocAlias.objects.create( - doc_alias=DocAlias.objects.get(name=name), - ipr=instance, - rev=rev) - else: - id = InternetDraft.objects.get(filename=draft[:-3]) - iprdraft = models.IprDraft(document=id, ipr=instance, revision=draft[-2:]) - iprdraft.save() + name = draft[:-3] + rev = draft[-2:] + + IprDocAlias.objects.create( + doc_alias=DocAlias.objects.get(name=name), + ipr=instance, + rev=rev) - # Save IprRfc(s) for rfcnum in form.cleaned_data["rfclist"].split(): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import DocAlias - models.IprDocAlias.objects.create( - doc_alias=DocAlias.objects.get(name="rfc%s" % int(rfcnum)), - ipr=instance, - rev="") - else: - rfc = Rfc.objects.get(rfc_number=int(rfcnum)) - iprrfc = models.IprRfc(document=rfc, ipr=instance) - iprrfc.save() + IprDocAlias.objects.create( + doc_alias=DocAlias.objects.get(name="rfc%s" % int(rfcnum)), + ipr=instance, + rev="") send_mail(request, settings.IPR_EMAIL_TO, ('IPR Submitter App', 'ietf-ipr@ietf.org'), 'New IPR Submission Notification', "ipr/new_update_email.txt", {"ipr": instance, "update": update}) return render("ipr/submitted.html", {"update": update}, context_instance=RequestContext(request)) @@ -328,12 +293,12 @@ def new(request, type, update=None, submitter=None): form = IprForm() form.unbound_form = True - # ietf.utils.log(dir(form.ietf_contact_is_submitter)) + # log(dir(form.ietf_contact_is_submitter)) return render("ipr/details_edit.html", {"ipr": form, "section_list":section_list, "debug": debug}, context_instance=RequestContext(request)) def update(request, ipr_id=None): """Update a specific IPR disclosure""" - ipr = get_object_or_404(models.IprDetail, ipr_id=ipr_id) + ipr = get_object_or_404(IprDetail, ipr_id=ipr_id) if not ipr.status in [1,3]: raise Http404 type = "specific" @@ -386,6 +351,3 @@ def get_ipr_summary(data): ipr = ", ".join(ipr[:-1]) + ", and " + ipr[-1] return ipr - -# changes done by convert-096.py:changed newforms to forms -# cleaned_data diff --git a/ietf/ipr/search.py b/ietf/ipr/search.py index 9ea73a09c..68fe4315b 100644 --- a/ietf/ipr/search.py +++ b/ietf/ipr/search.py @@ -3,28 +3,21 @@ import codecs import re import os.path + from django.http import HttpResponseRedirect, Http404 from django.shortcuts import render_to_response as render from django.template import RequestContext from django.conf import settings -from ietf.ipr.models import IprDraft, IprDetail + +from ietf.ipr.models import IprDocAlias, IprDetail from ietf.ipr.related import related_docs from ietf.utils import log, normalize_draftname from ietf.group.models import Group from ietf.doc.models import DocAlias - -def mark_last_doc(iprs): - for item in iprs: - docs = item.docs() - count = len(docs) - if count > 1: - item.last_draft = docs[count-1] - def iprs_from_docs(docs): iprs = [] for doc in docs: - from ietf.ipr.models import IprDocAlias disclosures = [ x.ipr for x in IprDocAlias.objects.filter(doc_alias=doc, ipr__status__in=[1,3]) ] doc.iprs = None if disclosures: @@ -57,7 +50,7 @@ def search(request, type="", q="", id=""): q = value if re.match(".*id", key): id = value - if type and q or id: + if (type and q) or id: #log("Got query: type=%s, q=%s, id=%s" % (type, q, id)) # Search by RFC number or draft-identifier @@ -82,14 +75,14 @@ def search(request, type="", q="", id=""): doc = str(first) docs = related_docs(first) iprs, docs = iprs_from_docs(docs) - iprs.sort(key=lambda x:(x.submitted_date,x.ipr_id)) - return render("ipr/search_doc_result.html", {"q": q, "first": first, "iprs": iprs, "docs": docs, "doc": doc }, + iprs.sort(key=lambda x: (x.submitted_date, x.ipr_id)) + return render("ipr/search_doc_result.html", {"q": q, "iprs": iprs, "docs": docs, "doc": doc }, context_instance=RequestContext(request) ) elif start.count(): return render("ipr/search_doc_list.html", {"q": q, "docs": start }, context_instance=RequestContext(request) ) else: - return render("ipr/search_doc_result.html", {"q": q, "first": {}, "iprs": {}, "docs": {}, "doc": doc }, + return render("ipr/search_doc_result.html", {"q": q, "iprs": {}, "docs": {}, "doc": doc }, context_instance=RequestContext(request) ) # Search by legal name @@ -98,9 +91,6 @@ def search(request, type="", q="", id=""): iprs = IprDetail.objects.filter(legal_name__icontains=q, status__in=[1,3]).order_by("-submitted_date", "-ipr_id") count = iprs.count() iprs = [ ipr for ipr in iprs if not ipr.updated_by.all() ] - # Some extra information, to help us render 'and' between the - # last two documents in a sequence - mark_last_doc(iprs) return render("ipr/search_holder_result.html", {"q": q, "iprs": iprs, "count": count }, context_instance=RequestContext(request) ) @@ -123,10 +113,7 @@ def search(request, type="", q="", id=""): iprs.append(ipr) count = len(iprs) iprs = [ ipr for ipr in iprs if not ipr.updated_by.all() ] - # Some extra information, to help us render 'and' between the - # last two documents in a sequence - iprs.sort(key=lambda x: x.ipr_id, reverse=True) # Reverse sort - mark_last_doc(iprs) + iprs.sort(key=lambda x: x.ipr_id, reverse=True) return render("ipr/search_patent_result.html", {"q": q, "iprs": iprs, "count": count }, context_instance=RequestContext(request) ) @@ -170,9 +157,6 @@ def search(request, type="", q="", id=""): iprs = IprDetail.objects.filter(title__icontains=q, status__in=[1,3]).order_by("-submitted_date", "-ipr_id") count = iprs.count() iprs = [ ipr for ipr in iprs if not ipr.updated_by.all() ] - # Some extra information, to help us render 'and' between the - # last two documents in a sequence - mark_last_doc(iprs) return render("ipr/search_iprtitle_result.html", {"q": q, "iprs": iprs, "count": count }, context_instance=RequestContext(request) ) diff --git a/ietf/ipr/urls.py b/ietf/ipr/urls.py index 43ad3749f..554215b0b 100644 --- a/ietf/ipr/urls.py +++ b/ietf/ipr/urls.py @@ -7,8 +7,8 @@ from django.views.generic.simple import redirect_to urlpatterns = patterns('', url(r'^$', views.showlist, name='ipr_showlist'), - (r'^about/$', views.default), - (r'^by-draft/$', views.list_drafts), + (r'^about/$', views.about), + (r'^by-draft/$', views.iprs_for_drafts_txt), url(r'^(?P\d+)/$', views.show, name='ipr_show'), (r'^update/$', redirect_to, { 'url': reverse_lazy('ipr_showlist') }), (r'^update/(?P\d+)/$', new.update), diff --git a/ietf/ipr/view_sections.py b/ietf/ipr/view_sections.py index 385b18af2..45b41f6f6 100644 --- a/ietf/ipr/view_sections.py +++ b/ietf/ipr/view_sections.py @@ -1,6 +1,5 @@ # Copyright The IETF Trust 2007, All Rights Reserved - section_table = { "specific": { "title": True, "specific": 1, "generic": 0, "third_party": 0, @@ -38,3 +37,15 @@ section_table = { "per_rfc_disclosure": False, "also_specific": False, }, } + +def section_list_for_ipr(ipr): + if ipr.legacy_url_0: + return section_table["legacy"] + elif ipr.generic: + #assert not ipr.third_party + return section_table["generic"] + elif ipr.third_party: + return section_table["third-party"] + else: + return section_table["specific"] + diff --git a/ietf/ipr/views.py b/ietf/ipr/views.py index 4cca6d8f1..baadc323c 100644 --- a/ietf/ipr/views.py +++ b/ietf/ipr/views.py @@ -1,18 +1,18 @@ # Copyright The IETF Trust 2007, All Rights Reserved +import os + from django.shortcuts import render_to_response as render, get_object_or_404 from django.template import RequestContext from django.template.loader import render_to_string from django.http import HttpResponse, Http404 from django.conf import settings -from ietf.idtracker.models import IETFWG -from ietf.ipr.models import IprDetail, SELECT_CHOICES, LICENSE_CHOICES -from ietf.ipr.view_sections import section_table -from ietf.utils import log -import os -def default(request): - """Default page, with links to sub-pages""" +from ietf.ipr.models import IprDetail, IprDocAlias, SELECT_CHOICES, LICENSE_CHOICES +from ietf.ipr.view_sections import section_list_for_ipr +from ietf.doc.models import Document + +def about(request): return render("ipr/disclosure.html", {}, context_instance=RequestContext(request)) def showlist(request): @@ -28,56 +28,6 @@ def showlist(request): 'thirdpty_disclosures': thirdpty_disclosures.order_by(* ['-submitted_date', ] ), }, context_instance=RequestContext(request) ) -def list_drafts(request): - iprs = IprDetail.objects.filter(status=1) - docipr = {} - docs = [] - for ipr in iprs: - for draft in ipr.drafts.all(): - name = draft.document.filename - if not name in docipr: - docipr[name] = [] - docipr[name] += [ ipr.ipr_id ] - for rfc in ipr.rfcs.all(): - name = "RFC%04d" % rfc.document.rfc_number - if not name in docipr: - docipr[name] = [] - docipr[name] += [ ipr.ipr_id ] - docs = [ {"name":key, "iprs":value, } for key,value in docipr.items() ] - return HttpResponse(render_to_string("ipr/drafts.html", { "docs":docs, }, - context_instance=RequestContext(request)), - mimetype="text/plain") - -def list_draftsREDESIGN(request): - from ietf.ipr.models import IprDocAlias - - docipr = {} - - for o in IprDocAlias.objects.filter(ipr__status=1).select_related("doc_alias"): - name = o.doc_alias.name - if name.startswith("rfc"): - name = name.upper() - - if not name in docipr: - docipr[name] = [] - - docipr[name].append(o.ipr_id) - - docs = [ dict(name=name, iprs=sorted(iprs)) for name, iprs in docipr.iteritems() ] - - # drafts.html is not an HTML file - return HttpResponse(render_to_string("ipr/drafts.html", - dict(docs=docs), - context_instance=RequestContext(request)), - mimetype="text/plain") - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - list_drafts = list_draftsREDESIGN - - -# Details views - def show(request, ipr_id=None, removed=None): """Show a specific IPR disclosure""" assert ipr_id != None @@ -87,9 +37,9 @@ def show(request, ipr_id=None, removed=None): context_instance=RequestContext(request)) if removed and ipr.status != 3: raise Http404 - if not ipr.status == 1 and not removed: + if ipr.status != 1 and not removed: raise Http404 - section_list = get_section_list(ipr) + section_list = section_list_for_ipr(ipr) contacts = ipr.contact.all() for contact in contacts: if contact.contact_type == 1: @@ -122,24 +72,30 @@ def show(request, ipr_id=None, removed=None): # if file does not exist, iframe is used instead pass - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.ipr.models import IprDraft, IprRfc - ipr.drafts = IprDraft.objects.filter(ipr=ipr).exclude(doc_alias__name__startswith="rfc").order_by("id") - ipr.rfcs = IprRfc.objects.filter(ipr=ipr).filter(doc_alias__name__startswith="rfc").order_by("id") + iprdocs = IprDocAlias.objects.filter(ipr=ipr).order_by("id").select_related("doc_alias", "doc_alias__document") + + ipr.drafts = [x for x in iprdocs if not x.doc_alias.name.startswith("rfc")] + ipr.rfcs = [x for x in iprdocs if x.doc_alias.name.startswith("rfc")] return render("ipr/details.html", {"ipr": ipr, "section_list": section_list}, context_instance=RequestContext(request)) +def iprs_for_drafts_txt(request): + docipr = {} -# ---- Helper functions ------------------------------------------------------ + for o in IprDocAlias.objects.filter(ipr__status=1).select_related("doc_alias"): + name = o.doc_alias.name + if name.startswith("rfc"): + name = name.upper() + + if not name in docipr: + docipr[name] = [] + + docipr[name].append(o.ipr_id) + + lines = [ u"# Machine-readable list of IPR disclosures by draft name" ] + for name, iprs in docipr.iteritems(): + lines.append(name + "\t" + "\t".join(unicode(ipr_id) for ipr_id in sorted(iprs))) + + return HttpResponse("\n".join(lines), mimetype="text/plain") -def get_section_list(ipr): - if ipr.legacy_url_0: - return section_table["legacy"] - elif ipr.generic: - #assert not ipr.third_party - return section_table["generic"] - elif ipr.third_party: - return section_table["third-party"] - else: - return section_table["specific"] diff --git a/ietf/secr/ipradmin/forms.py b/ietf/secr/ipradmin/forms.py index 7f601033b..a7d62d4c9 100644 --- a/ietf/secr/ipradmin/forms.py +++ b/ietf/secr/ipradmin/forms.py @@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe from django.forms.formsets import formset_factory from django.utils import simplejson -from ietf.ipr.models import IprDetail, IprContact, LICENSE_CHOICES, IprRfc, IprDraft, IprUpdate, SELECT_CHOICES, IprDocAlias +from ietf.ipr.models import IprDetail, IprContact, LICENSE_CHOICES, IprUpdate, SELECT_CHOICES, IprDocAlias from ietf.doc.models import DocAlias from ietf.secr.utils.document import get_rfc_num @@ -246,14 +246,7 @@ class IprDetailForm(BetterModelForm): obj.status_to_be = old_ipr.status obj.processed = 0 obj.save() - ''' - IprRfc.objects.filter(ipr=ipr_detail).delete() - IprDraft.objects.filter(ipr=ipr_detail).delete() - for rfc in self.cleaned_data['rfc_num']: - IprRfc.objects.create(ipr=ipr_detail, document=rfc) - for draft in self.cleaned_data['id_filename']: - IprDraft.objects.create(ipr=ipr_detail, document=draft) - ''' + IprDocAlias.objects.filter(ipr=ipr_detail).delete() for doc in self.cleaned_data['rfc_num']: IprDocAlias.objects.create(ipr=ipr_detail,doc_alias=doc) diff --git a/ietf/secr/ipradmin/models.py b/ietf/secr/ipradmin/models.py index e9c9bed54..137941ffa 100644 --- a/ietf/secr/ipradmin/models.py +++ b/ietf/secr/ipradmin/models.py @@ -1,193 +1 @@ -# Copyright The IETF Trust 2007, All Rights Reserved - from django.db import models -#from django import newforms as forms -#from sec.drafts.models import InternetDraft -#from sec.drafts.models import Rfc - -from ietf.ipr.models import * - -# ------------------------------------------------------------------------ -# Models - -''' -LICENSE_CHOICES = ( - (1, 'a) No License Required for Implementers.'), - (2, 'b) Royalty-Free, Reasonable and Non-Discriminatory License to All Implementers.'), - (3, 'c) Reasonable and Non-Discriminatory License to All Implementers with Possible Royalty/Fee.'), - (4, 'd) Licensing Declaration to be Provided Later (implies a willingness' - ' to commit to the provisions of a), b), or c) above to all implementers;' - ' otherwise, the next option "Unwilling to Commit to the Provisions of' - ' a), b), or c) Above". - must be selected).'), - (5, 'e) Unwilling to Commit to the Provisions of a), b), or c) Above.'), - (6, 'f) See Text Below for Licensing Declaration.'), -) -STDONLY_CHOICES = ( - (0, ""), - (1, "The licensing declaration is limited solely to standards-track IETF documents."), -) -SELECT_CHOICES = ( - (0, 'NO'), - (1, 'YES'), - (2, 'NO'), -) -STATUS_CHOICES = ( - ( 0, "Waiting for approval" ), - ( 1, "Approved and Posted" ), - ( 2, "Rejected by Administrator" ), - ( 3, "Removed by Request" ), -) -# not clear why this has both an ID and selecttype -# Also not clear why a table for "YES" and "NO". -class IprSelecttype(models.Model): - type_id = models.AutoField(primary_key=True) - is_pending = models.IntegerField(unique=True, db_column="selecttype") - type_display = models.CharField(blank=True, max_length=15) - def __str__(self): - return self.type_display - class Meta: - db_table = 'ipr_selecttype' - -class IprLicensing(models.Model): - licensing_option = models.AutoField(primary_key=True) - value = models.CharField(max_length=255, db_column='licensing_option_value') - def __str__(self): - return self.value; - class Meta: - db_table = 'ipr_licensing' - - -class IprDetail(models.Model): - ipr_id = models.AutoField(primary_key=True) - title = models.CharField(blank=True, db_column="document_title", max_length=255) - - # Legacy information fieldset - legacy_url_0 = models.CharField(blank=True, null=True, db_column="old_ipr_url", max_length=255) - legacy_url_1 = models.CharField(blank=True, null=True, db_column="additional_old_url1", max_length=255) - legacy_title_1 = models.CharField(blank=True, null=True, db_column="additional_old_title1", max_length=255) - legacy_url_2 = models.CharField(blank=True, null=True, db_column="additional_old_url2", max_length=255) - legacy_title_2 = models.CharField(blank=True, null=True, db_column="additional_old_title2", max_length=255) - - # Patent holder fieldset - legal_name = models.CharField("Legal Name", db_column="p_h_legal_name", max_length=255) - - # Patent Holder Contact fieldset - # self.contact.filter(contact_type=1) - - # IETF Contact fieldset - # self.contact.filter(contact_type=3) - - # Related IETF Documents fieldset - rfc_number = models.IntegerField(null=True, editable=False, blank=True) # always NULL - id_document_tag = models.IntegerField(null=True, editable=False, blank=True) # always NULL - other_designations = models.CharField(blank=True, max_length=255) - document_sections = models.TextField("Specific document sections covered", blank=True, max_length=255, db_column='disclouser_identify') - - # Patent Information fieldset - patents = models.TextField("Patent Applications", db_column="p_applications", max_length=255) - date_applied = models.CharField(max_length=255) - country = models.CharField(max_length=100) - notes = models.TextField("Additional notes", db_column="p_notes", blank=True) - # AMS Change - #is_pending = models.IntegerField("Unpublished Pending Patent Application", blank=True, choices=SELECT_CHOICES, db_column="selecttype") - #is_pending = models.BooleanField(db_column="selecttype") - is_pending = models.CharField(max_length=3,db_column="selecttype") - applies_to_all = models.IntegerField("Applies to all IPR owned by Submitter", blank=True, choices=SELECT_CHOICES, db_column="selectowned") - - # Licensing Declaration fieldset - #licensing_option = models.ForeignKey(IprLicensing, db_column='licensing_option') - licensing_option = models.IntegerField(null=True, blank=True, choices=LICENSE_CHOICES) - lic_opt_a_sub = models.IntegerField(editable=False, choices=STDONLY_CHOICES) - lic_opt_b_sub = models.IntegerField(editable=False, choices=STDONLY_CHOICES) - lic_opt_c_sub = models.IntegerField(editable=False, choices=STDONLY_CHOICES) - comments = models.TextField("Licensing Comments", blank=True) - lic_checkbox = models.BooleanField("All terms and conditions has been disclosed") - - - # Other notes fieldset - other_notes = models.TextField(blank=True) - - # Generated fields, not part of the submission form - # Hidden fields - third_party = models.BooleanField() - generic = models.BooleanField() - comply = models.BooleanField() - - status = models.IntegerField(null=True, blank=True, choices=STATUS_CHOICES) - submitted_date = models.DateField(blank=True) - update_notified_date = models.DateField(null=True, blank=True) - - def __str__(self): - return self.title - def docs(self): - return list(self.drafts.all()) + list(self.rfcs.all()) - def get_absolute_url(self): - return "/ipr/%d/" % self.ipr_id - def get_submitter(self): - try: - return self.contact.get(contact_type=3) - except IprContact.DoesNotExist: - return None - class Meta: - db_table = 'ipr_detail' - -class IprContact(models.Model): - TYPE_CHOICES = ( - (1, 'Patent Holder Contact'), - (2, 'IETF Participant Contact'), - (3, 'Submitter Contact'), - ) - contact_id = models.AutoField(primary_key=True) - ipr = models.ForeignKey(IprDetail, related_name="contact") - contact_type = models.IntegerField(choices=TYPE_CHOICES) - name = models.CharField(max_length=255) - title = models.CharField(blank=True, max_length=255) - department = models.CharField(blank=True, max_length=255) - address1 = models.CharField(blank=True, max_length=255) - address2 = models.CharField(blank=True, max_length=255) - telephone = models.CharField(max_length=25) - fax = models.CharField(blank=True, max_length=25) - email = models.EmailField(max_length=255) - def __str__(self): - return self.name or '' - class Meta: - db_table = 'ipr_contacts' - - -class IprDraft(models.Model): - ipr = models.ForeignKey(IprDetail, related_name='drafts') - document = models.ForeignKey(InternetDraft, db_column='id_document_tag', related_name="ipr") - revision = models.CharField(max_length=2) - def __str__(self): - return "%s which applies to %s-%s" % ( self.ipr, self.document, self.revision ) - class Meta: - db_table = 'ipr_ids' - -class IprNotification(models.Model): - ipr = models.ForeignKey(IprDetail) - notification = models.TextField(blank=True) - date_sent = models.DateField(null=True, blank=True) - time_sent = models.CharField(blank=True, max_length=25) - def __str__(self): - return "IPR notification for %s sent %s %s" % (self.ipr, self.date_sent, self.time_sent) - class Meta: - db_table = 'ipr_notifications' - -class IprRfc(models.Model): - ipr = models.ForeignKey(IprDetail, related_name='rfcs') - document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr") - def __str__(self): - return "%s applies to RFC%04d" % ( self.ipr, self.document_id ) - class Meta: - db_table = 'ipr_rfcs' - -class IprUpdate(models.Model): - id = models.IntegerField(primary_key=True) - ipr = models.ForeignKey(IprDetail, related_name='updates') - updated = models.ForeignKey(IprDetail, db_column='updated', related_name='updated_by') - status_to_be = models.IntegerField(null=True, blank=True) - processed = models.IntegerField(null=True, blank=True) - class Meta: - db_table = 'ipr_updates' - -''' diff --git a/ietf/secr/ipradmin/views.py b/ietf/secr/ipradmin/views.py index 7681de2da..8e0a325e6 100644 --- a/ietf/secr/ipradmin/views.py +++ b/ietf/secr/ipradmin/views.py @@ -15,7 +15,7 @@ from ietf.secr.ipradmin.forms import IprDetailForm, IPRContactFormset from ietf.secr.utils.document import get_rfc_num, is_draft import ietf.settings as settings -from ietf.ipr.models import IprDetail, IprUpdate, IprRfc, IprDraft, IprContact, LICENSE_CHOICES, STDONLY_CHOICES, IprNotification +from ietf.ipr.models import IprDetail, IprUpdate, IprContact, LICENSE_CHOICES, STDONLY_CHOICES, IprNotification from ietf.utils.mail import send_mail_text from ietf.doc.models import DocAlias @@ -145,14 +145,6 @@ def admin_notify(request, ipr_id): submitter_text = get_submitter_text(ipr_id, updated_ipr_id, from_page) document_relatives = '' - #drafts = IprDraft.objects.filter(ipr__ipr_id=ipr_id) - #for draft in drafts: - # document_relatives += get_document_relatives(ipr_id, draft, is_draft=1) - - #rfcs = IprRfc.objects.filter(ipr__ipr_id=ipr_id) - #for rfc in rfcs: - # document_relatives += get_document_relatives(ipr_id, rfc, is_draft=0) - # REDESIGN for iprdocalias in ipr_dtl.documents.all(): document_relatives += get_document_relatives(ipr_dtl, iprdocalias.doc_alias) diff --git a/ietf/templates/ipr/details.html b/ietf/templates/ipr/details.html index f63ae8181..d62c4edb6 100644 --- a/ietf/templates/ipr/details.html +++ b/ietf/templates/ipr/details.html @@ -162,15 +162,15 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% if ipr.rfclist %} RFC Numbers:{{ ipr.rfclist }} {% else %} - {% for doc in ipr.rfcs.all %} - RFC {{ doc.document.rfc_number }}:"{{ doc.document.title }}" + {% for iprdocalias in ipr.rfcs %} + RFC {{ iprdocalias.doc_alias.document.rfc_number }}:"{{ iprdocalias.doc_alias.document.title }}" {% endfor %} {% endif %} {% if ipr.draftlist %} I-D Filenames (draft-...):{{ ipr.draftlist }} {% else %} - {% for doc in ipr.drafts.all %} - Internet-Draft:"{{ doc.document.title }}"
      ({{ doc.document.filename }}{% if doc.revision %}-{{ doc.revision }}{% endif %}) + {% for iprdocalias in ipr.drafts %} + Internet-Draft:"{{ iprdocalias.doc_alias.document.title }}"
      ({{ iprdocalias.doc_alias.name }}{% if iprdocalias.rev %}-{{ iprdocalias.rev }}{% endif %}) {% endfor %} {% endif %} {% if ipr.other_designations %} diff --git a/ietf/templates/ipr/drafts.html b/ietf/templates/ipr/drafts.html deleted file mode 100644 index 07ca916ee..000000000 --- a/ietf/templates/ipr/drafts.html +++ /dev/null @@ -1,3 +0,0 @@ -# Machine-readable list of ipr disclosures by draft name -{% for doc in docs %}{{doc.name}}{% for num in doc.iprs %} {{ num }}{% endfor %} -{% endfor %} \ No newline at end of file diff --git a/ietf/templates/ipr/list.html b/ietf/templates/ipr/list.html index 7b9ebc6d2..a96f2bfa7 100644 --- a/ietf/templates/ipr/list.html +++ b/ietf/templates/ipr/list.html @@ -12,7 +12,7 @@ The IETF takes no position regarding the validity or scope of any intellectual property rights or other rights that might be claimed to pertain to the implementation or use of the technology described in any IETF documents or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights.

      -

      Click here to submit an IPR disclosure

      +

      Click here to submit an IPR disclosure

      Search the IPR Disclosures diff --git a/ietf/templates/ipr/list_item.html b/ietf/templates/ipr/list_item.html index 65d82183c..4f916ff68 100644 --- a/ietf/templates/ipr/list_item.html +++ b/ietf/templates/ipr/list_item.html @@ -3,17 +3,17 @@ {{ ipr.submitted_date }} {{ ipr.ipr_id }} - {% ifequal ipr.status 1 %} + {% if ipr.status == 1 %} {{ ipr.title|escape }} {% else %} {{ ipr.title|escape }}
      This IPR disclosure was removed at the request of the submitter. - {% endifequal %} + {% endif %}
      {% for item in ipr.updates.all %} - {% ifequal item.updated.status 1 %} + {% if item.updated.status == 1 %} Updates ID #{{ item.updated.ipr_id }}.
      - {% endifequal %} + {% endif %} {% endfor %} {% for item in ipr.updated_by.all %} {% if item.processed == 1 and item.ipr.status != 2 %} @@ -22,7 +22,7 @@ {% endfor %} - {% ifequal ipr.status 1 %} + {% if ipr.status == 1 %} {% if ipr.legacy_title_1 %} @@ -43,4 +43,4 @@ {% endif %} - {% endifequal %} + {% endif %} diff --git a/ietf/templates/ipr/removed.html b/ietf/templates/ipr/removed.html index 53a31604d..64dd488a0 100644 --- a/ietf/templates/ipr/removed.html +++ b/ietf/templates/ipr/removed.html @@ -1,9 +1,9 @@ {% extends "base.html" %} {# Copyright The IETF Trust 2009, All Rights Reserved #} -{% block title %}IPR Details - {{ ipr.title|escape }}{% endblock %} +{% block title %}IPR Details - {{ ipr.title }}{% endblock %} {% block content %} -

      {{ ipr.title|escape }}

      +

      {{ ipr.title }}

      This IPR disclosure was removed at the submitter's request. diff --git a/ietf/templates/ipr/search.html b/ietf/templates/ipr/search.html index 2693e5e89..d7bbf2697 100644 --- a/ietf/templates/ipr/search.html +++ b/ietf/templates/ipr/search.html @@ -11,12 +11,12 @@ label { float:left; width: 200px; }

      IPR Search

      Document Search

      -
      + - - - + + +
      -
      + @@ -61,13 +61,13 @@ label { float:left; width: 200px; }
      - + -
      + @@ -75,7 +75,7 @@ label { float:left; width: 200px; }
      * The search string must contain at least three characters, including at least one digit, and include punctuation marks. For best results, please enter the entire string, or as much of it as possible. -
      +
      -
      +
      -
      + diff --git a/ietf/templates/ipr/search_doc_result.html b/ietf/templates/ipr/search_doc_result.html index 2cef0e467..8e636289e 100644 --- a/ietf/templates/ipr/search_doc_result.html +++ b/ietf/templates/ipr/search_doc_result.html @@ -18,8 +18,8 @@ - Search result on {{ doc.name|rfcspace|lstrip:"0"|rfcnospace }}, "{{ doc.document.title|escape }}"{% ifnotequal doc first %}{% if doc.related %}, that was {{ doc.relation|lower }} {{ doc.related.source|rfcspace|lstrip:"0"|rfcnospace }}, "{{ doc.related.source.title }}"{% endif %} - {% endifnotequal %} + Search result on {{ doc.name|rfcspace|lstrip:"0"|rfcnospace }}, "{{ doc.document.title|escape }}"{% if not forloop.first %}{% if doc.related %}, that was {{ doc.relation|lower }} {{ doc.related.source|rfcspace|lstrip:"0"|rfcnospace }}, "{{ doc.related.source.title }}"{% endif %} + {% endif %} {% if doc.iprs %} @@ -35,7 +35,7 @@ No IPR disclosures have been submitted directly on {{ doc.name|rfcspace|lstrip:"0" }}{% if iprs %}, - but there are disclosures on {% ifequal docs|length 2 %}a related document{% else %}related documents{% endifequal %}, listed on this page{% endif %}. + but there are disclosures on {% if docs|length == 2 %}a related document{% else %}related documents{% endif %}, listed on this page{% endif %}. diff --git a/ietf/templates/ipr/search_result.html b/ietf/templates/ipr/search_result.html index 7ca31e583..622a89fc3 100644 --- a/ietf/templates/ipr/search_result.html +++ b/ietf/templates/ipr/search_result.html @@ -23,16 +23,18 @@ {% block intro_prefix %}IPR that was submitted by {{ q }}, and{% endblock %} - {% block related %} - {% if not ipr.docs %} - is not related to a specific IETF contribution. - {% else %} - is related to - {% for item in ipr.docs %} - {% ifequal item ipr.last_draft %} and {% endifequal %} - {{ item.document }}, "{{ item.document.title|escape }},"{% if item.document.related %}, {{ item.document.relation }} {{ item.document.related }}, "{{ item.document.related.title|escape }}"{% endif %} - {% endfor %} - {% endif %} + {% block related %} + {% with ipr.docs as docs %} + {% if not docs %} + is not related to a specific IETF contribution. + {% else %} + is related to + {% for item in docs %} + {% if forloop.last and forloop.counter > 1 %}and{% endif %} + {{ item.document }}, "{{ item.document.title|escape }},"{% if item.document.related %}, {{ item.document.relation }} {{ item.document.related }}, "{{ item.document.related.title|escape }}"{% endif %} + {% endfor %} + {% endif %} + {% endwith %} {% endblock %} {% block intro_suffix %}{% endblock %} diff --git a/ietf/templates/ipr/search_wg_result.html b/ietf/templates/ipr/search_wg_result.html index 527428b6d..a49f86313 100644 --- a/ietf/templates/ipr/search_wg_result.html +++ b/ietf/templates/ipr/search_wg_result.html @@ -31,9 +31,9 @@
    • ID # {{ ipr.ipr_id }}
    • {% for item in ipr.updates.all %} - {% ifequal item.updated.status 1 %} + {% if item.updated.status == 1 %} IPR disclosure ID# {{ item.updated.ipr_id }}, "{{ item.updated.title|escape }}" Updated by - {% endifequal %} + {% endif %} {% endfor %} "{{ ipr.title|escape }}" From 19da33d8ffc641ec1107dff1abf809e21f878d2d Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 2 Dec 2013 17:29:17 +0000 Subject: [PATCH 126/173] Make the search for specific draft name in IPR support both drafts and RFCs, clean up some related cruft - Legacy-Id: 6775 --- ietf/iesg/views.py | 8 - ietf/ipr/search.py | 60 ++++--- .../doc/search/ipr_column_with_label.html | 39 +---- ietf/templates/ipr/search.html | 150 +++++++++--------- ietf/templates/ipr/search_doc_list.html | 2 +- 5 files changed, 108 insertions(+), 151 deletions(-) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 85796ab40..a241b03cf 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -357,14 +357,6 @@ def agenda_documents(request): sections = agenda_sections() fill_in_agenda_docs(date, sections, docs_by_date[d]) - for doc in docs_by_date[d]: - if doc.type_id=='draft': - if doc.get_state_slug() != "rfc": - doc.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(doc.name) - else: - doc.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(doc.rfc_number()) - doc.iprCount = len(doc.ipr()) - telechats.append({ "date":date, "sections": sorted((num, section) for num, section in sections.iteritems() diff --git a/ietf/ipr/search.py b/ietf/ipr/search.py index 68fe4315b..cf071401d 100644 --- a/ietf/ipr/search.py +++ b/ietf/ipr/search.py @@ -39,38 +39,34 @@ def patent_file_search(url, q): return q in text return False -def search(request, type="", q="", id=""): +def search(request): wgs = Group.objects.filter(type="wg").select_related().order_by("acronym") - args = request.REQUEST.items() - if args: - for key, value in args: - if key == "option": - type = value - if re.match(".*search", key): - q = value - if re.match(".*id", key): - id = value - if (type and q) or id: - #log("Got query: type=%s, q=%s, id=%s" % (type, q, id)) + search_type = request.GET.get("option") + if search_type: + docid = request.GET.get("id") or request.GET.get("id_document_tag") or "" + + q = "" + for key, value in request.GET.items(): + if key.endswith("search"): + q = value + + if search_type and (q or docid): # Search by RFC number or draft-identifier # Document list with IPRs - if type in ["document_search", "rfc_search"]: + if search_type in ["document_search", "rfc_search"]: doc = q - if type == "document_search": - if q: + + if docid: + start = DocAlias.objects.filter(name=docid) + else: + if search_type == "document_search": q = normalize_draftname(q) start = DocAlias.objects.filter(name__contains=q, name__startswith="draft") - if id: - start = DocAlias.objects.filter(name=id) - if type == "rfc_search": - if q: - try: - q = int(q, 10) - except: - q = -1 - start = DocAlias.objects.filter(name__contains=q, name__startswith="rfc") - if start.count() == 1: + elif search_type == "rfc_search": + start = DocAlias.objects.filter(name="rfc%s" % q.lstrip("0")) + + if len(start) == 1: first = start[0] doc = str(first) docs = related_docs(first) @@ -78,7 +74,7 @@ def search(request, type="", q="", id=""): iprs.sort(key=lambda x: (x.submitted_date, x.ipr_id)) return render("ipr/search_doc_result.html", {"q": q, "iprs": iprs, "docs": docs, "doc": doc }, context_instance=RequestContext(request) ) - elif start.count(): + elif start: return render("ipr/search_doc_list.html", {"q": q, "docs": start }, context_instance=RequestContext(request) ) else: @@ -87,7 +83,7 @@ def search(request, type="", q="", id=""): # Search by legal name # IPR list with documents - elif type == "patent_search": + elif search_type == "patent_search": iprs = IprDetail.objects.filter(legal_name__icontains=q, status__in=[1,3]).order_by("-submitted_date", "-ipr_id") count = iprs.count() iprs = [ ipr for ipr in iprs if not ipr.updated_by.all() ] @@ -96,7 +92,7 @@ def search(request, type="", q="", id=""): # Search by content of email or pagent_info field # IPR list with documents - elif type == "patent_info_search": + elif search_type == "patent_info_search": if len(q) < 3: return render("ipr/search_error.html", {"q": q, "error": "The search string must contain at least three characters" }, context_instance=RequestContext(request) ) @@ -119,7 +115,7 @@ def search(request, type="", q="", id=""): # Search by wg acronym # Document list with IPRs - elif type == "wg_search": + elif search_type == "wg_search": docs = list(DocAlias.objects.filter(document__group__acronym=q)) related = [] for doc in docs: @@ -136,7 +132,7 @@ def search(request, type="", q="", id=""): # Search by rfc and id title # Document list with IPRs - elif type == "title_search": + elif search_type == "title_search": docs = list(DocAlias.objects.filter(document__title__icontains=q)) related = [] for doc in docs: @@ -153,7 +149,7 @@ def search(request, type="", q="", id=""): # Search by title of IPR disclosure # IPR list with documents - elif type == "ipr_title_search": + elif search_type == "ipr_title_search": iprs = IprDetail.objects.filter(title__icontains=q, status__in=[1,3]).order_by("-submitted_date", "-ipr_id") count = iprs.count() iprs = [ ipr for ipr in iprs if not ipr.updated_by.all() ] @@ -161,7 +157,7 @@ def search(request, type="", q="", id=""): context_instance=RequestContext(request) ) else: - raise Http404("Unexpected search type in IPR query: %s" % type) + raise Http404("Unexpected search type in IPR query: %s" % search_type) return HttpResponseRedirect(request.path) return render("ipr/search.html", {"wgs": wgs}, context_instance=RequestContext(request)) diff --git a/ietf/templates/doc/search/ipr_column_with_label.html b/ietf/templates/doc/search/ipr_column_with_label.html index 434460ddd..3582ccd6a 100644 --- a/ietf/templates/doc/search/ipr_column_with_label.html +++ b/ietf/templates/doc/search/ipr_column_with_label.html @@ -1,36 +1,5 @@ -{% comment %} -Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of the Nokia Corporation and/or its - subsidiary(-ies) nor the names of its contributors may be used - to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -{% endcomment %} -{% load ietf_filters %} -{% if doc.iprCount %} IPR:{{ doc.iprCount }} {% endif %} + + {% if doc.type_id == "draft" and doc.ipr %} + IPR: {{ doc.ipr|length }} + {% endif %} diff --git a/ietf/templates/ipr/search.html b/ietf/templates/ipr/search.html index d7bbf2697..29fdff2a1 100644 --- a/ietf/templates/ipr/search.html +++ b/ietf/templates/ipr/search.html @@ -11,92 +11,92 @@ label { float:left; width: 200px; }

      IPR Search

      Document Search

      - - + + + + + + - - - - + -
      - - - - -
      + function check_numeric(val) { + if (IsNumeric(val)) { + return true; + } else { + alert ("Please enter numerics only"); + return false; + } + return false; + } + // --> + +
      + + + + +

      Keyword Search

      -
      - - - - -
      -
      - - - - -
      - * The search string must contain at least three characters, including at least one digit, and include punctuation marks. For best results, please enter the entire string, or as much of it as possible. +
      + + + + +
      +
      + + + + +
      -
      - - - - -
      -
      - - - - -
      -
      - - - - -
      + * The search string must contain at least three characters, including at least one digit, and include punctuation marks. For best results, please enter the entire string, or as much of it as possible. + +
      + + + + +
      +
      + + + + +
      +
      + + + + +

      Back to IPR Disclosure Page

      diff --git a/ietf/templates/ipr/search_doc_list.html b/ietf/templates/ipr/search_doc_list.html index df3f64b98..c117740c8 100644 --- a/ietf/templates/ipr/search_doc_list.html +++ b/ietf/templates/ipr/search_doc_list.html @@ -8,7 +8,7 @@

      Please select one of following I-Ds

      {% endblock %} From cadedce0234a00c33e6b1062978a258cca22fd23 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 2 Dec 2013 18:11:01 +0000 Subject: [PATCH 127/173] Port cookies/ away from use of |equal filter, remove |equal filter - Legacy-Id: 6776 --- ietf/doc/templatetags/ietf_filters.py | 4 ---- ietf/templates/cookies/settings.html | 30 +++++++++++++-------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 638d739f7..30d7f792e 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -410,10 +410,6 @@ def expires_soon(x,request): days = 14 return x > -days -@register.filter(name='equal') -def equal(x, y): - return str(x)==str(y) - @register.filter(name='startswith') def startswith(x, y): return unicode(x).startswith(y) diff --git a/ietf/templates/cookies/settings.html b/ietf/templates/cookies/settings.html index 783ca9b2d..24260a590 100644 --- a/ietf/templates/cookies/settings.html +++ b/ietf/templates/cookies/settings.html @@ -4,7 +4,7 @@ {% block title %}User settings{% endblock %} {% block content %} -

      Cookie settings for the ietf datatracker.

      +

      Cookie settings for the IETF datatracker

      Following settings are implemented using cookies, so if you have cookies disabled then you are not able to change the settings @@ -22,12 +22,12 @@ cookies disabled then you are not able to change the settings - {% if new_enough|equal:"7" %}7 days{%else%}7 days{% endif %} - {% if new_enough|equal:"14" %}14 days{%else%}14 days{% endif %} - {% if new_enough|equal:"21" %}21 days{%else%}21 days{% endif %} - {% if new_enough|equal:"30" %}30 days{%else%}30 days{% endif %} - {% if new_enough|equal:"60" %}60 days{%else%}60 days{% endif %} - {% if new_enough|equal:"90" %}90 days{%else%}90 days{% endif %} + {% if new_enough == 7 %}7 days{%else%}7 days{% endif %} + {% if new_enough == 14 %}14 days{%else%}14 days{% endif %} + {% if new_enough == 21 %}21 days{%else%}21 days{% endif %} + {% if new_enough == 30 %}30 days{%else%}30 days{% endif %} + {% if new_enough == 60 %}60 days{%else%}60 days{% endif %} + {% if new_enough == 90 %}90 days{%else%}90 days{% endif %} @@ -41,12 +41,12 @@ cookies disabled then you are not able to change the settings - {% if expires_soon|equal:"7" %}7 days{%else%}7 days{% endif %} - {% if expires_soon|equal:"14" %}14 days{%else%}14 days{% endif %} - {% if expires_soon|equal:"21" %}21 days{%else%}21 days{% endif %} - {% if expires_soon|equal:"30" %}30 days{%else%}30 days{% endif %} - {% if expires_soon|equal:"60" %}60 days{%else%}60 days{% endif %} - {% if expires_soon|equal:"90" %}90 days{%else%}90 days{% endif %} + {% if expires_soon == 7 %}7 days{%else%}7 days{% endif %} + {% if expires_soon == 14 %}14 days{%else%}14 days{% endif %} + {% if expires_soon == 21 %}21 days{%else%}21 days{% endif %} + {% if expires_soon == 30 %}30 days{%else%}30 days{% endif %} + {% if expires_soon == 60 %}60 days{%else%}60 days{% endif %} + {% if expires_soon == 90 %}90 days{%else%}90 days{% endif %} @@ -60,8 +60,8 @@ cookies disabled then you are not able to change the settings - {% if full_draft|equal:"off" %}off{%else%}off{% endif %} - {% if full_draft|equal:"on" %}on{%else%}on{% endif %} + {% if full_draft == "off" %}off{%else%}off{% endif %} + {% if full_draft == "on" %}on{%else%}on{% endif %} {% endblock %} From 6c56f410416a3eee0b81920bb4ded25319798996 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 2 Dec 2013 18:12:16 +0000 Subject: [PATCH 128/173] Fix broken indentation - Legacy-Id: 6777 --- ietf/cookies/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/cookies/views.py b/ietf/cookies/views.py index 98eadaa4b..d2703c8cc 100644 --- a/ietf/cookies/views.py +++ b/ietf/cookies/views.py @@ -51,7 +51,7 @@ def expires_soon(request, days="14"): def full_draft(request, enabled="off"): if enabled != "on" and enabled != "off": - enabled = "off" + enabled = "off" response = settings(request, -1, -1, enabled) response.set_cookie("full_draft", enabled, 315360000) return response From 7852e092faf162e2d4aeada124599c88bd2cb483 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Dec 2013 09:56:18 +0000 Subject: [PATCH 129/173] Remove unused IprLicensing and IprSelecttype classes from IPR models - Legacy-Id: 6778 --- ietf/ipr/admin.py | 8 -------- ietf/ipr/models.py | 21 +-------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/ietf/ipr/admin.py b/ietf/ipr/admin.py index 8cc9c81e3..0a3b6da3f 100644 --- a/ietf/ipr/admin.py +++ b/ietf/ipr/admin.py @@ -24,18 +24,10 @@ class IprDetailAdmin(admin.ModelAdmin): return u", ".join(res) admin.site.register(IprDetail, IprDetailAdmin) -class IprLicensingAdmin(admin.ModelAdmin): - pass -admin.site.register(IprLicensing, IprLicensingAdmin) - class IprNotificationAdmin(admin.ModelAdmin): pass admin.site.register(IprNotification, IprNotificationAdmin) -class IprSelecttypeAdmin(admin.ModelAdmin): - pass -admin.site.register(IprSelecttype, IprSelecttypeAdmin) - class IprUpdateAdmin(admin.ModelAdmin): pass admin.site.register(IprUpdate, IprUpdateAdmin) diff --git a/ietf/ipr/models.py b/ietf/ipr/models.py index 30e0c6941..ca3276d7d 100644 --- a/ietf/ipr/models.py +++ b/ietf/ipr/models.py @@ -21,10 +21,7 @@ STDONLY_CHOICES = ( (1, "The licensing declaration is limited solely to standards-track IETF documents."), ) SELECT_CHOICES = ( - ("0", 'NO'), - ("1", 'YES'), - ("2", 'NO'), - (0, 'NO'), # with new schema, choices are really numeric + (0, 'NO'), (1, 'YES'), (2, 'NO'), ) @@ -34,21 +31,6 @@ STATUS_CHOICES = ( ( 2, "Rejected by Administrator" ), ( 3, "Removed by Request" ), ) -# not clear why this has both an ID and selecttype -# Also not clear why a table for "YES" and "NO". -class IprSelecttype(models.Model): - type_id = models.AutoField(primary_key=True) - is_pending = models.IntegerField(unique=True, db_column="selecttype") - type_display = models.CharField(blank=True, max_length=15) - def __unicode__(self): - return self.type_display - -class IprLicensing(models.Model): - licensing_option = models.AutoField(primary_key=True) - value = models.CharField(max_length=255, db_column='licensing_option_value') - def __unicode__(self): - return self.value; - class IprDetail(models.Model): ipr_id = models.AutoField(primary_key=True) @@ -85,7 +67,6 @@ class IprDetail(models.Model): applies_to_all = models.IntegerField("Applies to all IPR owned by Submitter", blank=True, null=True, choices=SELECT_CHOICES, db_column="selectowned") # Licensing Declaration fieldset - #licensing_option = models.ForeignKey(IprLicensing, db_column='licensing_option') licensing_option = models.IntegerField(null=True, blank=True, choices=LICENSE_CHOICES) lic_opt_a_sub = models.IntegerField(null=True, editable=False, choices=STDONLY_CHOICES) lic_opt_b_sub = models.IntegerField(null=True, editable=False, choices=STDONLY_CHOICES) From bb2d070679e5f984d8da2db2149138810802c7b2 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Dec 2013 18:33:07 +0000 Subject: [PATCH 130/173] Redo tests for IPR to cover all views, fix some bugs and add some finishing touches - Legacy-Id: 6779 --- ietf/doc/tests_draft.py | 16 +- ietf/ipr/admin.py | 16 +- ietf/ipr/models.py | 17 +- ietf/ipr/new.py | 40 +- ietf/ipr/search.py | 4 +- ietf/ipr/tests.py | 372 +++++++++++++----- ietf/ipr/testurl.list | 73 ---- ietf/ipr/urls.py | 4 - ietf/ipr/views.py | 2 +- ietf/secr/ipradmin/forms.py | 4 +- ietf/secr/ipradmin/views.py | 6 +- ietf/templates/ipr/details.html | 88 +++-- .../templates/ipr/search_doctitle_result.html | 6 +- ietf/templates/ipr/search_result.html | 25 +- ietf/utils/test_data.py | 5 + ietf/wgcharter/views.py | 3 +- ietf/wginfo/edit.py | 1 - ietf/wginfo/milestones.py | 1 - 18 files changed, 396 insertions(+), 287 deletions(-) delete mode 100644 ietf/ipr/testurl.list diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index dc746f9ac..f11383ec9 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -1,7 +1,5 @@ -import unittest import StringIO -import os, shutil -from datetime import date, timedelta, time +import os, shutil, datetime from django.core.urlresolvers import reverse as urlreverse from django.conf import settings @@ -10,7 +8,7 @@ from pyquery import PyQuery import debug from ietf.doc.models import * -from ietf.doc .utils import * +from ietf.doc.utils import * from ietf.name.models import * from ietf.group.models import * from ietf.person.models import * @@ -452,11 +450,11 @@ class ExpireIDsTests(TestCase): second_cut_off = Meeting.get_second_cut_off() ietf_monday = Meeting.get_ietf_monday() - self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(second_cut_off - datetime.timedelta(days=7), time(0, 0, 0)))) - self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(second_cut_off, time(0, 0, 0)))) - self.assertTrue(in_draft_expire_freeze(datetime.datetime.combine(second_cut_off + datetime.timedelta(days=7), time(0, 0, 0)))) - self.assertTrue(in_draft_expire_freeze(datetime.datetime.combine(ietf_monday - datetime.timedelta(days=1), time(0, 0, 0)))) - self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(ietf_monday, time(0, 0, 0)))) + self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(second_cut_off - datetime.timedelta(days=7), datetime.time(0, 0, 0)))) + self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(second_cut_off, datetime.time(0, 0, 0)))) + self.assertTrue(in_draft_expire_freeze(datetime.datetime.combine(second_cut_off + datetime.timedelta(days=7), datetime.time(0, 0, 0)))) + self.assertTrue(in_draft_expire_freeze(datetime.datetime.combine(ietf_monday - datetime.timedelta(days=1), datetime.time(0, 0, 0)))) + self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(ietf_monday, datetime.time(0, 0, 0)))) def test_warn_expirable_drafts(self): from ietf.doc.expire import get_soon_to_expire_drafts, send_expire_warning_for_draft diff --git a/ietf/ipr/admin.py b/ietf/ipr/admin.py index 0a3b6da3f..41fa7ca16 100644 --- a/ietf/ipr/admin.py +++ b/ietf/ipr/admin.py @@ -12,16 +12,8 @@ class IprDetailAdmin(admin.ModelAdmin): search_fields = ['title', 'legal_name'] def docs(self, ipr): - res = [] - for iprdocalias in IprDocAlias.objects.filter(ipr=ipr).order_by("id").select_related("doc_alias"): - if iprdocalias.doc_alias.name.startswith("rfc"): - n = iprdocalias.doc_alias.name.upper() - elif iprdocalias.rev: - n = iprdocalias.doc_alias.name + iprdocalias.rev - else: - n = iprdocalias.doc_alias.name - res.append(n) - return u", ".join(res) + return u", ".join(a.formatted_name() for a in IprDocAlias.objects.filter(ipr=ipr).order_by("id").select_related("doc_alias")) + admin.site.register(IprDetail, IprDetailAdmin) class IprNotificationAdmin(admin.ModelAdmin): @@ -32,4 +24,6 @@ class IprUpdateAdmin(admin.ModelAdmin): pass admin.site.register(IprUpdate, IprUpdateAdmin) -admin.site.register(IprDocAlias) +class IprDocAliasAdmin(admin.ModelAdmin): + raw_id_fields = ["ipr", "doc_alias"] +admin.site.register(IprDocAlias, IprDocAliasAdmin) diff --git a/ietf/ipr/models.py b/ietf/ipr/models.py index ca3276d7d..c8655b94d 100644 --- a/ietf/ipr/models.py +++ b/ietf/ipr/models.py @@ -90,9 +90,11 @@ class IprDetail(models.Model): def __unicode__(self): return self.title + @models.permalink def get_absolute_url(self): return ('ietf.ipr.views.show', [str(self.ipr_id)]) + def get_submitter(self): try: return self.contact.get(contact_type=3) @@ -101,6 +103,9 @@ class IprDetail(models.Model): except IprContact.MultipleObjectsReturned: return self.contact.filter(contact_type=3)[0] + def docs(self): + return self.iprdocalias_set.select_related("doc_alias", "doc_alias__document").order_by("id") + class IprContact(models.Model): TYPE_CHOICES = ( (1, 'Patent Holder Contact'), @@ -138,9 +143,19 @@ class IprUpdate(models.Model): class IprDocAlias(models.Model): - ipr = models.ForeignKey(IprDetail, related_name='documents') + ipr = models.ForeignKey(IprDetail) doc_alias = models.ForeignKey(DocAlias) rev = models.CharField(max_length=2, blank=True) + + def formatted_name(self): + name = self.doc_alias.name + if name.startswith("rfc"): + return name.upper() + elif self.rev: + return "%s-%s" % (name, self.rev) + else: + return name + def __unicode__(self): if self.rev: return u"%s which applies to %s-%s" % (self.ipr, self.doc_alias.name, self.rev) diff --git a/ietf/ipr/new.py b/ietf/ipr/new.py index a1524898b..54e7c1c76 100644 --- a/ietf/ipr/new.py +++ b/ietf/ipr/new.py @@ -1,6 +1,6 @@ # Copyright The IETF Trust 2007, All Rights Reserved -import re +import re, datetime from django.shortcuts import render_to_response as render, get_object_or_404 from django.template import RequestContext @@ -10,6 +10,7 @@ from django import forms from ietf.utils import log from ietf.utils.mail import send_mail +from ietf.doc.models import Document, DocAlias from ietf.ipr.models import IprDetail, IprDocAlias, IprContact, LICENSE_CHOICES, IprUpdate from ietf.ipr.view_sections import section_table @@ -61,8 +62,6 @@ def new(request, type, update=None, submitter=None): one form containing fields from 4 tables -- don't build something like this again... """ - debug = "" - section_list = section_table[type].copy() section_list.update({"title":False, "new_intro":False, "form_intro":True, "form_submit":True, "form_legend": True, }) @@ -131,7 +130,7 @@ def new(request, type, update=None, submitter=None): for rfc in rfclist: try: DocAlias.objects.get(name="rfc%s" % int(rfc)) - except: + except (DocAlias.DoesNotExist, DocAlias.MultipleObjectsReturned, ValueError): raise forms.ValidationError("Unknown RFC number: %s - please correct this." % rfc) rfclist = " ".join(rfclist) return rfclist @@ -145,19 +144,19 @@ def new(request, type, update=None, submitter=None): if draft.endswith(".txt"): draft = draft[:-4] if re.search("-[0-9][0-9]$", draft): - filename = draft[:-3] + name = draft[:-3] rev = draft[-2:] else: - filename = draft + name = draft rev = None try: - doc = Document.objects.get(docalias__name=filename) - except Exception as e: + doc = Document.objects.get(docalias__name=name) + except (Document.DoesNotExist, Document.MultipleObjectsReturned) as e: log("Exception: %s" % e) - raise forms.ValidationError("Unknown Internet-Draft: %s - please correct this." % filename) + raise forms.ValidationError("Unknown Internet-Draft: %s - please correct this." % name) if rev and doc.rev != rev: - raise forms.ValidationError("Unexpected revision '%s' for draft %s - the current revision is %s. Please check this." % (rev, filename, id.revision)) - drafts.append("%s-%s" % (filename, doc.rev)) + raise forms.ValidationError("Unexpected revision '%s' for draft %s - the current revision is %s. Please check this." % (rev, name, doc.rev)) + drafts.append("%s-%s" % (name, doc.rev)) return " ".join(drafts) return "" def clean_licensing_option(self): @@ -178,11 +177,8 @@ def new(request, type, update=None, submitter=None): # POST of the "get updater" form, so we don't want to validate # this one. When we're posting *this* form, submitter is None, # even when updating. - if (request.method == 'POST' or '_testpost' in request.REQUEST) and not submitter: - if request.method == 'POST': - data = request.POST.copy() - else: - data = request.GET.copy() + if request.method == 'POST' and not submitter: + data = request.POST.copy() data["submitted_date"] = datetime.datetime.now().strftime("%Y-%m-%d") data["third_party"] = section_list["third_party"] data["generic"] = section_list["generic"] @@ -205,13 +201,13 @@ def new(request, type, update=None, submitter=None): # Save IprDetail instance = form.save(commit=False) - legal_name_genitive = data['legal_name'] + "'" if data['legal_name'].endswith('s') else data['legal_name'] + "'s" + legal_name_genitive = data['legal_name'] + "'" if data['legal_name'].endswith('s') else data['legal_name'] + "'s" if type == "generic": instance.title = legal_name_genitive + " General License Statement" - if type == "specific": + elif type == "specific": data["ipr_summary"] = get_ipr_summary(form.cleaned_data) instance.title = legal_name_genitive + """ Statement about IPR related to %(ipr_summary)s""" % data - if type == "third-party": + elif type == "third-party": data["ipr_summary"] = get_ipr_summary(form.cleaned_data) ietf_name_genitive = data['ietf_name'] + "'" if data['ietf_name'].endswith('s') else data['ietf_name'] + "'s" instance.title = ietf_name_genitive + """ Statement about IPR related to %(ipr_summary)s belonging to %(legal_name)s""" % data @@ -294,7 +290,7 @@ def new(request, type, update=None, submitter=None): form.unbound_form = True # log(dir(form.ietf_contact_is_submitter)) - return render("ipr/details_edit.html", {"ipr": form, "section_list":section_list, "debug": debug}, context_instance=RequestContext(request)) + return render("ipr/details_edit.html", {"ipr": form, "section_list":section_list}, context_instance=RequestContext(request)) def update(request, ipr_id=None): """Update a specific IPR disclosure""" @@ -320,9 +316,7 @@ def update(request, ipr_id=None): if request.method == 'POST': form = UpdateForm(request.POST) - elif '_testpost' in request.REQUEST: - form = UpdateForm(request.GET) - else: + else: form = UpdateForm() if not(form.is_valid()): diff --git a/ietf/ipr/search.py b/ietf/ipr/search.py index cf071401d..bd79e02f2 100644 --- a/ietf/ipr/search.py +++ b/ietf/ipr/search.py @@ -85,12 +85,12 @@ def search(request): # IPR list with documents elif search_type == "patent_search": iprs = IprDetail.objects.filter(legal_name__icontains=q, status__in=[1,3]).order_by("-submitted_date", "-ipr_id") - count = iprs.count() + count = len(iprs) iprs = [ ipr for ipr in iprs if not ipr.updated_by.all() ] return render("ipr/search_holder_result.html", {"q": q, "iprs": iprs, "count": count }, context_instance=RequestContext(request) ) - # Search by content of email or pagent_info field + # Search by patents field or content of emails for patent numbers # IPR list with documents elif search_type == "patent_info_search": if len(q) < 3: diff --git a/ietf/ipr/tests.py b/ietf/ipr/tests.py index baeb70492..261f33214 100644 --- a/ietf/ipr/tests.py +++ b/ietf/ipr/tests.py @@ -1,101 +1,281 @@ -# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -# All rights reserved. Contact: Pasi Eronen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the Nokia Corporation and/or its -# subsidiary(-ies) nor the names of its contributors may be used -# to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import os, datetime, shutil + +import urllib + +from pyquery import PyQuery -import os -import unittest -from django.test.client import Client from django.conf import settings -from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, canonicalize_sitemap -from ietf.utils.mail import outbox, empty_outbox +from django.core.urlresolvers import reverse as urlreverse -class IprUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) - def doCanonicalize(self, url, content): - if url.startswith("/feed/"): - return canonicalize_feed(content) - elif url == "/sitemap-ipr.xml": - return canonicalize_sitemap(content) - else: - return content +from ietf.utils.test_utils import TestCase, login_testing_unauthorized +from ietf.utils.test_data import make_test_data +from ietf.utils.mail import outbox +from ietf.ipr.models import * -# this test should be ported to run on a test database instead of the -# real database, and possibly expanded -# class NewIprTestCase(unittest.TestCase,RealDatabaseTest): -# SPECIFIC_DISCLOSURE = { -# 'legal_name':'Testing Only Please Ignore', -# 'hold_name':'Test Holder', -# 'hold_telephone':'555-555-0100', -# 'hold_email':'test.holder@example.com', -# 'ietf_name':'Test Participant', -# 'ietf_telephone':'555-555-0101', -# 'ietf_email':'test.participant@example.com', -# 'rfclist':'1149', -# 'draftlist':'draft-burdis-http-sasl-00', -# 'patents':'none', -# 'date_applied':'never', -# 'country':'nowhere', -# 'licensing_option':'5', -# 'subm_name':'Test Submitter', -# 'subm_telephone':'555-555-0102', -# 'subm_email':'test.submitter@example.com' -# } -# -# def setUp(self): -# self.setUpRealDatabase() -# def tearDown(self): -# self.tearDownRealDatabase() -# -# def testNewSpecific(self): -# print " Testing IPR disclosure submission" -# empty_outbox() -# c = Client() -# response = c.post('/ipr/new-specific/', self.SPECIFIC_DISCLOSURE) -# self.assertEquals(response.status_code, 200) -# self.assert_("Your IPR disclosure has been submitted" in response.content) -# self.assertEquals(len(outbox), 1) -# print "OK (1 email found in test outbox)" + +class IprTests(TestCase): + def setUp(self): + # for patent number search + self.ipr_dir = os.path.abspath("tmp-ipr-dir") + if not os.path.exists(self.ipr_dir): + os.mkdir(self.ipr_dir) + settings.IPR_DOCUMENT_PATH = self.ipr_dir + + def tearDown(self): + shutil.rmtree(self.ipr_dir) + + def test_overview(self): + make_test_data() + ipr = IprDetail.objects.get(title="Statement regarding rights") + + r = self.client.get(urlreverse("ipr_showlist")) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + def test_iprs_for_drafts(self): + draft = make_test_data() + ipr = IprDetail.objects.get(title="Statement regarding rights") + + r = self.client.get(urlreverse("ietf.ipr.views.iprs_for_drafts_txt")) + self.assertEqual(r.status_code, 200) + self.assertTrue(draft.name in r.content) + self.assertTrue(str(ipr.pk) in r.content) + + def test_about(self): + r = self.client.get(urlreverse("ietf.ipr.views.about")) + self.assertEqual(r.status_code, 200) + self.assertTrue("File a disclosure" in r.content) + + def test_search(self): + draft = make_test_data() + ipr = IprDetail.objects.get(title="Statement regarding rights") + + url = urlreverse("ipr_search") + + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(q("form input[name=document_search]")) + + # find by id + r = self.client.get(url + "?option=document_search&id=%s" % draft.name) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + # find draft + r = self.client.get(url + "?option=document_search&document_search=%s" % draft.name) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + # search + select document + r = self.client.get(url + "?option=document_search&document_search=draft") + self.assertEqual(r.status_code, 200) + self.assertTrue(draft.name in r.content) + self.assertTrue(ipr.title not in r.content) + + DocAlias.objects.create(name="rfc321", document=draft) + + # find RFC + r = self.client.get(url + "?option=rfc_search&rfc_search=321") + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + # find by patent owner + r = self.client.get(url + "?option=patent_search&patent_search=%s" % ipr.legal_name) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) - -class IprFileTestCase(unittest.TestCase): - def testFileExistence(self): - print " Testing if IPR disclosure files exist locally" - fpath = os.path.join(settings.IPR_DOCUMENT_PATH, "juniper-ipr-RFC-4875.txt") - if not os.path.exists(fpath): - print "\nERROR: IPR disclosure files not found in "+settings.IPR_DOCUMENT_PATH - print "They are needed for testing IPR searching." - print "Download them to a local directory with:" - print "wget -nd -nc -np -r ftp://ftp.ietf.org/ietf/IPR/" - print "And set IPR_DOCUMENT_PATH in settings_local.py\n" - else: - print "OK (they seem to exist)" - + # find by patent info + r = self.client.get(url + "?option=patent_info_search&patent_info_search=%s" % ipr.patents) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + # find by patent info in file + filename = "ipr1.txt" + with open(os.path.join(self.ipr_dir, filename), "w") as f: + f.write("Hello world\nPTO9876") + ipr.legacy_url_0 = "/hello/world/%s" % filename + ipr.save() + + r = self.client.get(url + "?option=patent_info_search&patent_info_search=PTO9876") + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + # must search for at least 3 characters with digit + r = self.client.get(url + "?option=patent_info_search&patent_info_search=a") + self.assertTrue("ipr search result error" in r.content.lower()) + + r = self.client.get(url + "?option=patent_info_search&patent_info_search=aaa") + self.assertTrue("ipr search result error" in r.content.lower()) + + # find by group acronym + r = self.client.get(url + "?option=wg_search&wg_search=%s" % draft.group.acronym) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + # find by doc title + r = self.client.get(url + "?option=title_search&title_search=%s" % urllib.quote(draft.title)) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + # find by ipr title + r = self.client.get(url + "?option=ipr_title_search&ipr_title_search=%s" % urllib.quote(ipr.title)) + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + def test_feed(self): + make_test_data() + ipr = IprDetail.objects.get(title="Statement regarding rights") + + r = self.client.get("/feed/ipr/") + self.assertEqual(r.status_code, 200) + self.assertTrue(ipr.title in r.content) + + def test_sitemap(self): + make_test_data() + ipr = IprDetail.objects.get(title="Statement regarding rights") + + r = self.client.get("/sitemap-ipr.xml") + self.assertEqual(r.status_code, 200) + self.assertTrue("/ipr/%s/" % ipr.pk in r.content) + + def test_new_generic(self): + draft = make_test_data() + + url = urlreverse("ietf.ipr.new.new", kwargs={ "type": "generic" }) + + # faulty post + r = self.client.post(url, { + "legal_name": "Test Legal", + }) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q("ul.errorlist")) > 0) + + # succesfull post + r = self.client.post(url, { + "legal_name": "Test Legal", + "hold_name": "Test Holder", + "hold_telephone": "555-555-0100", + "hold_email": "test.holder@example.com", + "ietf_name": "Test Participant", + "ietf_telephone": "555-555-0101", + "ietf_email": "test.participant@example.com", + "patents": "none", + "date_applied": "never", + "country": "nowhere", + "licensing_option": "5", + "subm_name": "Test Submitter", + "subm_telephone": "555-555-0102", + "subm_email": "test.submitter@example.com" + }) + self.assertEqual(r.status_code, 200) + self.assertTrue("Your IPR disclosure has been submitted" in r.content) + + iprs = IprDetail.objects.filter(title__icontains="General License Statement") + self.assertEqual(len(iprs), 1) + ipr = iprs[0] + self.assertEqual(ipr.legal_name, "Test Legal") + self.assertEqual(ipr.status, 0) + + def test_new_specific(self): + draft = make_test_data() + + url = urlreverse("ietf.ipr.new.new", kwargs={ "type": "specific" }) + + # succesfull post + r = self.client.post(url, { + "legal_name": "Test Legal", + "hold_name": "Test Holder", + "hold_telephone": "555-555-0100", + "hold_email": "test.holder@example.com", + "ietf_name": "Test Participant", + "ietf_telephone": "555-555-0101", + "ietf_email": "test.participant@example.com", + "rfclist": DocAlias.objects.filter(name__startswith="rfc")[0].name[3:], + "draftlist": "%s-%s" % (draft.name, draft.rev), + "patents": "none", + "date_applied": "never", + "country": "nowhere", + "licensing_option": "5", + "subm_name": "Test Submitter", + "subm_telephone": "555-555-0102", + "subm_email": "test.submitter@example.com" + }) + self.assertEqual(r.status_code, 200) + self.assertTrue("Your IPR disclosure has been submitted" in r.content) + + iprs = IprDetail.objects.filter(title__icontains=draft.name) + self.assertEqual(len(iprs), 1) + ipr = iprs[0] + self.assertEqual(ipr.legal_name, "Test Legal") + self.assertEqual(ipr.status, 0) + + def test_new_thirdparty(self): + draft = make_test_data() + + url = urlreverse("ietf.ipr.new.new", kwargs={ "type": "third-party" }) + + # succesfull post + r = self.client.post(url, { + "legal_name": "Test Legal", + "hold_name": "Test Holder", + "hold_telephone": "555-555-0100", + "hold_email": "test.holder@example.com", + "ietf_name": "Test Participant", + "ietf_telephone": "555-555-0101", + "ietf_email": "test.participant@example.com", + "rfclist": "", + "draftlist": "%s-%s" % (draft.name, draft.rev), + "patents": "none", + "date_applied": "never", + "country": "nowhere", + "licensing_option": "5", + "subm_name": "Test Submitter", + "subm_telephone": "555-555-0102", + "subm_email": "test.submitter@example.com" + }) + self.assertEqual(r.status_code, 200) + self.assertTrue("Your IPR disclosure has been submitted" in r.content) + + iprs = IprDetail.objects.filter(title__icontains="belonging to Test Legal") + self.assertEqual(len(iprs), 1) + ipr = iprs[0] + self.assertEqual(ipr.legal_name, "Test Legal") + self.assertEqual(ipr.status, 0) + + def test_update(self): + draft = make_test_data() + original_ipr = IprDetail.objects.get(title="Statement regarding rights") + + url = urlreverse("ietf.ipr.new.update", kwargs={ "ipr_id": original_ipr.pk }) + + # succesfull post + r = self.client.post(url, { + "legal_name": "Test Legal", + "hold_name": "Test Holder", + "hold_telephone": "555-555-0100", + "hold_email": "test.holder@example.com", + "ietf_name": "Test Participant", + "ietf_telephone": "555-555-0101", + "ietf_email": "test.participant@example.com", + "rfclist": "", + "draftlist": "%s-%s" % (draft.name, draft.rev), + "patents": "none", + "date_applied": "never", + "country": "nowhere", + "licensing_option": "5", + "subm_name": "Test Submitter", + "subm_telephone": "555-555-0102", + "subm_email": "test.submitter@example.com" + }) + self.assertEqual(r.status_code, 200) + self.assertTrue("Your IPR disclosure has been submitted" in r.content) + + iprs = IprDetail.objects.filter(title__icontains=draft.name) + self.assertEqual(len(iprs), 1) + ipr = iprs[0] + self.assertEqual(ipr.legal_name, "Test Legal") + self.assertEqual(ipr.status, 0) + + self.assertTrue(ipr.updates.filter(updated=original_ipr)) diff --git a/ietf/ipr/testurl.list b/ietf/ipr/testurl.list deleted file mode 100644 index ca755d894..000000000 --- a/ietf/ipr/testurl.list +++ /dev/null @@ -1,73 +0,0 @@ -200 /ipr/ -200 /ipr/657/ # Generic disclosure -200 /ipr/564/ # Generic submitted by email -200 /ipr/834/ # Specific disclosure -200 /ipr/1121/ # Specific disclosure submitted by email (PDF) -200 /ipr/1173/ # Specific disclosure submitted by email (TXT) -200 /ipr/795/ # Third-party disclosure -200 /ipr/865/ # Third party disclosure submitted by email (TXT) -200 /ipr/1140/ # Non-ASCII patent holder name -200 /ipr/1069/ # Non-ASCII title -200 /ipr/1129/ # Non-ASCII other fields -200 /ipr/751/ # Updates others, and is updated by others -200 /ipr/765/ # Removed -404 /ipr/1066/ # Test record - -200 /ipr/new-generic/ -200 /ipr/new-specific/ -200 /ipr/new-third-party/ -200 /ipr/new-generic/?_testpost=1 -200 /ipr/new-specific/?_testpost=1 -200 /ipr/new-third-party/?_testpost=1 - -301 /ipr/update/ -200 /ipr/update/657/ # Generic -200 /ipr/update/820/ # Third-party -200 /ipr/update/844/ # Specific -404 /ipr/update/1066/ # Removed test record - -200 /ipr/update/657/?_testpost=1 # Generic -200 /ipr/update/820/?_testpost=1 # Third-party -200 /ipr/update/844/?_testpost=1 # Specific -200 /ipr/update/657/?_testpost=1&email=test@example.com&name=Test&telephone=123&update_auth=on -200 /ipr/update/820/?_testpost=1&email=test@example.com&name=Test&telephone=123&update_auth=on -200 /ipr/update/844/?_testpost=1&email=test@example.com&name=Test&telephone=123&update_auth=on - -200 /ipr/search/ -302 /ipr/search/?option=document_search # incomplete argument set gives redirect - -200 /ipr/search/?document_search=mod&option=document_search # Returns document list -200 /ipr/search/?id_document_tag=2220&option=document_search # Simple case -200 /ipr/search/?id_document_tag=2221&option=document_search # Empty result -200 /ipr/search/?id_document_tag=2221x&option=document_search # Non-numeric -200 /ipr/search/?option=document_search&document_search=draft-housley-tls-authz-extns-05 # More complex result - -200 /ipr/search/?rfc_search=1034&option=rfc_search # Loong result -200 /ipr/search/?rfc_search=4555&option=rfc_search # Simple result -200 /ipr/search/?rfc_search=4444&option=rfc_search # Empty result -200 /ipr/search/?rfc_search=4xyz&option=rfc_search # non-numeric - -200 /ipr/search/?patent_search=nortel&option=patent_search -200 /ipr/search/?patent_search=nortelxz&option=patent_search # Empty result - -200 /ipr/search/?wg_search=dnsext&option=wg_search -200 /ipr/search/?wg_search=aaa&option=wg_search -200 /ipr/search/?wg_search=acct&option=wg_search # Empty result - -200 /ipr/search/?option=title_search&title_search=AAA -200 /ipr/search/?option=title_search&title_search=AAAxz # Empty result - -200 /ipr/search/?patent_info_search=123&option=patent_info_search -200 /ipr/search/?patent_info_search=31415&option=patent_info_search # Empty result -200 /ipr/search/?patent_info_search=12&option=patent_info_search # Error: at least 3 characters -200 /ipr/search/?patent_info_search=abc&option=patent_info_search # Error: at least 1 digit - -200 /ipr/search/?option=ipr_title_search&ipr_title_search=nortel -200 /ipr/search/?option=ipr_title_search&ipr_title_search=nortelxz # Empty result -404 /ipr/search/?id_document_tag=12345 # no search type: 404 - -200 /ipr/about/ -200 /ipr/by-draft/ - -200 /feed/ipr/ -200 /sitemap-ipr.xml diff --git a/ietf/ipr/urls.py b/ietf/ipr/urls.py index 554215b0b..e27c38224 100644 --- a/ietf/ipr/urls.py +++ b/ietf/ipr/urls.py @@ -17,7 +17,3 @@ urlpatterns = patterns('', (r'^new-(?Pthird-party)/$', new.new), url(r'^search/$', search.search, name="ipr_search"), ) - - - - diff --git a/ietf/ipr/views.py b/ietf/ipr/views.py index baadc323c..bc6df1076 100644 --- a/ietf/ipr/views.py +++ b/ietf/ipr/views.py @@ -20,7 +20,7 @@ def showlist(request): generic_disclosures = disclosures.filter(status__in=[1,3], generic=1) specific_disclosures = disclosures.filter(status__in=[1,3], generic=0, third_party=0) thirdpty_disclosures = disclosures.filter(status__in=[1,3], generic=0, third_party=1) - + return render("ipr/list.html", { 'generic_disclosures' : generic_disclosures.order_by(* ['-submitted_date', ] ), diff --git a/ietf/secr/ipradmin/forms.py b/ietf/secr/ipradmin/forms.py index a7d62d4c9..ef37d1fe8 100644 --- a/ietf/secr/ipradmin/forms.py +++ b/ietf/secr/ipradmin/forms.py @@ -148,11 +148,11 @@ class IprDetailForm(BetterModelForm): self.fields['updated'].initial = updates[0].updated.ipr_id rfcs = {} - for rfc in self.instance.documents.filter(doc_alias__name__startswith='rfc'): + for rfc in self.instance.docs().filter(doc_alias__name__startswith='rfc'): rfcs[rfc.doc_alias.id] = get_rfc_num(rfc.doc_alias.document)+" "+rfc.doc_alias.document.title drafts = {} - for draft in self.instance.documents.exclude(doc_alias__name__startswith='rfc'): + for draft in self.instance.docs().exclude(doc_alias__name__startswith='rfc'): drafts[draft.doc_alias.id] = draft.doc_alias.document.name self.initial['rfc_num'] = simplejson.dumps(rfcs) self.initial['id_filename'] = simplejson.dumps(drafts) diff --git a/ietf/secr/ipradmin/views.py b/ietf/secr/ipradmin/views.py index 8e0a325e6..b1a24c73a 100644 --- a/ietf/secr/ipradmin/views.py +++ b/ietf/secr/ipradmin/views.py @@ -145,7 +145,7 @@ def admin_notify(request, ipr_id): submitter_text = get_submitter_text(ipr_id, updated_ipr_id, from_page) document_relatives = '' - for iprdocalias in ipr_dtl.documents.all(): + for iprdocalias in ipr_dtl.docs(): document_relatives += get_document_relatives(ipr_dtl, iprdocalias.doc_alias) return dict( @@ -520,8 +520,8 @@ def admin_detail(request, ipr_id): # conversion #rfcs = ipr_dtl.rfcs.all() #drafts = ipr_dtl.drafts.all() - rfcs = ipr_dtl.documents.filter(doc_alias__name__startswith='rfc') - drafts = ipr_dtl.documents.exclude(doc_alias__name__startswith='rfc') + rfcs = ipr_dtl.docs().filter(doc_alias__name__startswith='rfc') + drafts = ipr_dtl.docs().exclude(doc_alias__name__startswith='rfc') titles_data, rfcs_data, drafts_data, designations_data = (), (), (), () rfc_titles, draft_titles = [], [] if rfcs: diff --git a/ietf/templates/ipr/details.html b/ietf/templates/ipr/details.html index d62c4edb6..c657da35f 100644 --- a/ietf/templates/ipr/details.html +++ b/ietf/templates/ipr/details.html @@ -21,8 +21,10 @@ table.ipr { margin-top: 1em; } {% endblock %} {% block content %} + {% load ietf_filters %} + {% if section_list.title %} -

      {{ ipr.title|escape }}

      +

      {{ ipr.title }}

      {% endif %} @@ -59,15 +61,15 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% for item in ipr.updates.all %}

      This IPR disclosure updates IPR disclosure ID #{{ item.updated.ipr_id }}, - {% ifequal item.updated.status 0 %} - "{{ item.updated.title|escape }}". + {% if item.updated.status == 0 %} + "{{ item.updated.title }}". {% else %} - {% ifequal item.updated.status 1 %} - "{{ item.updated.title|escape }}". + {% if item.updated.status == 1 %} + "{{ item.updated.title }}". {% else %} - "{{ item.updated.title|escape }}", which was removed at the request of the submitter. - {% endifequal %} - {% endifequal %} + "{{ item.updated.title }}", which was removed at the request of the submitter. + {% endif %} + {% endif %}

      {% endfor %} @@ -75,11 +77,11 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% if item.processed == 1 and item.ipr.status != 2 %}

      This IPR disclosure has been updated by IPR disclosure ID #{{ item.ipr.ipr_id }}, - {% ifequal item.status_to_be 1 %} - "{{ item.ipr.title|escape }}". + {% if item.status_to_be == 1 %} + "{{ item.ipr.title }}". {% else %} - "{{ item.ipr.title|escape }}", which was removed at the request of the submitter. - {% endifequal %} + "{{ item.ipr.title }}", which was removed at the request of the submitter. + {% endif %}

      {% endif %} {% endfor %} @@ -103,7 +105,7 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided - Legal Name: {{ ipr.legal_name|escape }} + Legal Name: {{ ipr.legal_name }} {% endif %} @@ -115,14 +117,14 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided Patent Holder's Contact for License Application - Name: {{ ipr.holder_contact.name|escape }} - Title: {{ ipr.holder_contact.title|escape }} - Department: {{ ipr.holder_contact.department|escape }} - Address1: {{ ipr.holder_contact.address1|escape }} - Address2: {{ ipr.holder_contact.address2|escape }} - Telephone: {{ ipr.holder_contact.telephone|escape }} - Fax: {{ ipr.holder_contact.fax|escape }} - Email: {{ ipr.holder_contact.email|escape }} + Name: {{ ipr.holder_contact.name }} + Title: {{ ipr.holder_contact.title }} + Department: {{ ipr.holder_contact.department }} + Address1: {{ ipr.holder_contact.address1 }} + Address2: {{ ipr.holder_contact.address2 }} + Telephone: {{ ipr.holder_contact.telephone }} + Fax: {{ ipr.holder_contact.fax }} + Email: {{ ipr.holder_contact.email }} {% endif %} @@ -136,14 +138,14 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% if ipr.ietf_contact.name %} - Name: {{ ipr.ietf_contact.name|escape }} - Title: {{ ipr.ietf_contact.title|escape }} - Department: {{ ipr.ietf_contact.department|escape }} - Address1: {{ ipr.ietf_contact.address1|escape }} - Address2: {{ ipr.ietf_contact.address2|escape }} - Telephone: {{ ipr.ietf_contact.telephone|escape }} - Fax: {{ ipr.ietf_contact.fax|escape }} - Email: {{ ipr.ietf_contact.email|escape }} + Name: {{ ipr.ietf_contact.name }} + Title: {{ ipr.ietf_contact.title }} + Department: {{ ipr.ietf_contact.department }} + Address1: {{ ipr.ietf_contact.address1 }} + Address2: {{ ipr.ietf_contact.address2 }} + Telephone: {{ ipr.ietf_contact.telephone }} + Fax: {{ ipr.ietf_contact.fax }} + Email: {{ ipr.ietf_contact.email }} {% else %} No information submitted {% endif %} @@ -163,14 +165,14 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided RFC Numbers:{{ ipr.rfclist }} {% else %} {% for iprdocalias in ipr.rfcs %} - RFC {{ iprdocalias.doc_alias.document.rfc_number }}:"{{ iprdocalias.doc_alias.document.title }}" + {{ iprdocalias.formatted_name|rfcspace }}:"{{ iprdocalias.doc_alias.document.title }}" {% endfor %} {% endif %} {% if ipr.draftlist %} I-D Filenames (draft-...):{{ ipr.draftlist }} {% else %} {% for iprdocalias in ipr.drafts %} - Internet-Draft:"{{ iprdocalias.doc_alias.document.title }}"
      ({{ iprdocalias.doc_alias.name }}{% if iprdocalias.rev %}-{{ iprdocalias.rev }}{% endif %}) + Internet-Draft:"{{ iprdocalias.doc_alias.document.title }}"
      ({{ iprdocalias.formatted_name }}) {% endfor %} {% endif %} {% if ipr.other_designations %} @@ -200,7 +202,7 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided Date(s) granted or applied for: {{ ipr.date_applied }} Country: {{ ipr.country }} - Additional Notes:{{ ipr.notes|escape|linebreaks }} + Additional Notes:{{ ipr.notes|linebreaks }} B. Does this disclosure relate to an unpublished pending patent application?: @@ -228,7 +230,7 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% if ipr.document_sections %} - {{ ipr.document_sections|escape|linebreaks }} + {{ ipr.document_sections|linebreaks }} {% else %} No information submitted {% endif %} @@ -283,7 +285,7 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% if ipr.comments %} - {{ ipr.comments|escape|linebreaks }} + {{ ipr.comments|linebreaks }} {% else %} No information submitted {% endif %} @@ -353,14 +355,14 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% endif %} {% endif %} - Name: {{ ipr.submitter.name|escape }} - Title: {{ ipr.submitter.title|escape }} - Department: {{ ipr.submitter.department|escape }} - Address1: {{ ipr.submitter.address1|escape }} - Address2: {{ ipr.submitter.address2|escape }} - Telephone: {{ ipr.submitter.telephone|escape }} - Fax: {{ ipr.submitter.fax|escape }} - Email: {{ ipr.submitter.email|escape }} + Name: {{ ipr.submitter.name }} + Title: {{ ipr.submitter.title }} + Department: {{ ipr.submitter.department }} + Address1: {{ ipr.submitter.address1 }} + Address2: {{ ipr.submitter.address2 }} + Telephone: {{ ipr.submitter.telephone }} + Fax: {{ ipr.submitter.fax }} + Email: {{ ipr.submitter.email }} {% else %} No information submitted {% endif %} @@ -377,7 +379,7 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided {% if ipr.other_notes %} - {{ ipr.other_notes|escape|linebreaks }} + {{ ipr.other_notes|linebreaks }} {% else %} No information submitted {% endif %} diff --git a/ietf/templates/ipr/search_doctitle_result.html b/ietf/templates/ipr/search_doctitle_result.html index 66ea47d6b..988545d17 100644 --- a/ietf/templates/ipr/search_doctitle_result.html +++ b/ietf/templates/ipr/search_doctitle_result.html @@ -19,7 +19,7 @@ - IPR that is related to {{ alias.name|rfcspace|lstrip:"0"|rfcnospace }}, "{{ alias.document.title|escape }}"{% if alias.related %}, that was {{ alias.relation|lower }} {{ alias.related.source.name|rfcspace|lstrip:"0"|rfcnospace }}, "{{ alias.related.source.title }}"{% endif %} + IPR that is related to {{ alias.name|rfcspace|lstrip:"0"|rfcnospace }}, "{{ alias.document.title }}"{% if alias.related %}, that was {{ alias.relation|lower }} {{ alias.related.source.name|rfcspace|lstrip:"0"|rfcnospace }}, "{{ alias.related.source.title }}"{% endif %} @@ -31,10 +31,10 @@ {% for item in ipr.updated_by.all %} {% if item.processed == 1 and item.ipr.status != 2 %} - IPR disclosure ID# {{ item.ipr.ipr_id }} "{{ item.ipr.title|escape }}" Updates + IPR disclosure ID# {{ item.ipr.ipr_id }} "{{ item.ipr.title }}" Updates {% endif %} {% endfor %} - "{{ ipr.title|escape }}" + "{{ ipr.title }}" {% endfor %} diff --git a/ietf/templates/ipr/search_result.html b/ietf/templates/ipr/search_result.html index 622a89fc3..9345e2e5b 100644 --- a/ietf/templates/ipr/search_result.html +++ b/ietf/templates/ipr/search_result.html @@ -5,6 +5,7 @@ {% block doctype %}{% endblock %} {% block title %}IPR Search Result{% endblock %} {% block content %} +{% load ietf_filters %}

      IPR Disclosures

      {% block search_result %} @@ -24,37 +25,37 @@ - + {% for item in ipr.updates.all %} - {% ifnotequal item ipr %} + {% if item != ipr %} - {% endifnotequal %} + {% endif %} {% endfor %} {% endfor %} diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index 67df82345..63e9fbc99 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -387,10 +387,15 @@ def make_test_data(): lic_opt_a_sub=2, lic_opt_b_sub=2, lic_opt_c_sub=2, + patents="PTO12345", + date_applied="foo", + country="Whole World", comments="", lic_checkbox=True, other_notes="", status=1, + generic=0, + third_party=0, submitted_date=datetime.date.today(), ) diff --git a/ietf/wgcharter/views.py b/ietf/wgcharter/views.py index 9a6b74bd9..a9831cf11 100644 --- a/ietf/wgcharter/views.py +++ b/ietf/wgcharter/views.py @@ -4,7 +4,6 @@ from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound from django.shortcuts import render_to_response, get_object_or_404, redirect from django.template.loader import render_to_string from django.core.urlresolvers import reverse as urlreverse -from django.core.urlresolvers import reverse from django.template import RequestContext from django import forms from django.forms.util import ErrorList @@ -319,7 +318,7 @@ def edit_ad(request, name): charter.time = e.time charter.save() - return HttpResponseRedirect(reverse('doc_view', kwargs={'name': charter.name})) + return redirect('doc_view', name=charter.name) else: init = { "ad" : charter.ad_id } form = AdForm(initial=init) diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index 69441242c..b14839c89 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -4,7 +4,6 @@ import re, os, datetime, shutil from django.shortcuts import render_to_response, get_object_or_404, redirect from django.http import HttpResponseForbidden -from django.core.urlresolvers import reverse from django.template import RequestContext from django import forms from django.utils import simplejson diff --git a/ietf/wginfo/milestones.py b/ietf/wginfo/milestones.py index dee1765cc..a83f2303b 100644 --- a/ietf/wginfo/milestones.py +++ b/ietf/wginfo/milestones.py @@ -3,7 +3,6 @@ import re, os, string, datetime, shutil, calendar from django.shortcuts import render_to_response, get_object_or_404, redirect -from django.core.urlresolvers import reverse from django.template import RequestContext from django import forms from django.http import HttpResponse, HttpResponseForbidden, HttpResponseBadRequest From 28abe3dc0dc18a73b61491fa2bbb801250491427 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Dec 2013 18:35:29 +0000 Subject: [PATCH 131/173] Remove unused idtracker.models import - Legacy-Id: 6780 --- ietf/secr/sreq/tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ietf/secr/sreq/tests.py b/ietf/secr/sreq/tests.py index 4aec3f2f6..7c25962fe 100644 --- a/ietf/secr/sreq/tests.py +++ b/ietf/secr/sreq/tests.py @@ -8,7 +8,6 @@ from ietf.group.models import Group from ietf.ietfauth.decorators import has_role from ietf.utils.test_data import make_test_data from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest -from ietf.idtracker.models import Role from urlparse import urlsplit #from pyquery import PyQuery From 26ec475d89c2272be5cebff0c313457dad7ac6db Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Dec 2013 18:53:04 +0000 Subject: [PATCH 132/173] Fix spelling mistake in comments - Legacy-Id: 6781 --- ietf/ipr/tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ietf/ipr/tests.py b/ietf/ipr/tests.py index 261f33214..a6af02600 100644 --- a/ietf/ipr/tests.py +++ b/ietf/ipr/tests.py @@ -152,7 +152,7 @@ class IprTests(TestCase): q = PyQuery(r.content) self.assertTrue(len(q("ul.errorlist")) > 0) - # succesfull post + # successful post r = self.client.post(url, { "legal_name": "Test Legal", "hold_name": "Test Holder", @@ -183,7 +183,7 @@ class IprTests(TestCase): url = urlreverse("ietf.ipr.new.new", kwargs={ "type": "specific" }) - # succesfull post + # successful post r = self.client.post(url, { "legal_name": "Test Legal", "hold_name": "Test Holder", @@ -216,7 +216,7 @@ class IprTests(TestCase): url = urlreverse("ietf.ipr.new.new", kwargs={ "type": "third-party" }) - # succesfull post + # successful post r = self.client.post(url, { "legal_name": "Test Legal", "hold_name": "Test Holder", @@ -250,7 +250,7 @@ class IprTests(TestCase): url = urlreverse("ietf.ipr.new.update", kwargs={ "ipr_id": original_ipr.pk }) - # succesfull post + # successful post r = self.client.post(url, { "legal_name": "Test Legal", "hold_name": "Test Holder", From 6c57fe496f941749c2972f469703bce98aa0a55a Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Dec 2013 18:55:26 +0000 Subject: [PATCH 133/173] Port mailinglists to new schema - Legacy-Id: 6782 --- ietf/mailinglists/tests.py | 63 +++++++++---------- ietf/mailinglists/testurl.list | 4 -- ietf/mailinglists/urls.py | 9 +-- ietf/mailinglists/views.py | 10 +++ ...gwebmail_list.html => group_archives.html} | 25 ++++---- 5 files changed, 52 insertions(+), 59 deletions(-) delete mode 100644 ietf/mailinglists/testurl.list rename ietf/templates/mailinglists/{wgwebmail_list.html => group_archives.html} (57%) diff --git a/ietf/mailinglists/tests.py b/ietf/mailinglists/tests.py index 043584ea0..83659a9f6 100644 --- a/ietf/mailinglists/tests.py +++ b/ietf/mailinglists/tests.py @@ -1,38 +1,31 @@ -# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -# All rights reserved. Contact: Pasi Eronen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the Nokia Corporation and/or its -# subsidiary(-ies) nor the names of its contributors may be used -# to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from django.core.urlresolvers import reverse as urlreverse -from ietf.utils.test_utils import SimpleUrlTestCase +from pyquery import PyQuery + +from ietf.utils.test_utils import TestCase +from ietf.utils.test_data import make_test_data +from ietf.utils.mail import outbox + + +class MailingListTests(TestCase): + def test_groups(self): + draft = make_test_data() + group = draft.group + url = urlreverse("ietf.mailinglists.views.groups") + + # only those with an archive + group.list_archive = "" + group.save() + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q(".group-archives a:contains(\"%s\")" % group.acronym)), 0) + + # successful get + group.list_archive = "https://example.com/foo" + group.save() + r = self.client.get(url) + q = PyQuery(r.content) + self.assertEqual(len(q(".group-archives a:contains(\"%s\")" % group.acronym)), 1) -class MailingListsUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) diff --git a/ietf/mailinglists/testurl.list b/ietf/mailinglists/testurl.list deleted file mode 100644 index 2e9153518..000000000 --- a/ietf/mailinglists/testurl.list +++ /dev/null @@ -1,4 +0,0 @@ -200 /list/wg/ -301 /list/nonwg/ -301 /list/nonwg/update/ -301 /list/request/ diff --git a/ietf/mailinglists/urls.py b/ietf/mailinglists/urls.py index 2787cf30e..9795b63de 100644 --- a/ietf/mailinglists/urls.py +++ b/ietf/mailinglists/urls.py @@ -1,14 +1,9 @@ # Copyright The IETF Trust 2007, All Rights Reserved from django.conf.urls.defaults import patterns -from ietf.idtracker.models import IETFWG -http_archive_wg_queryset = IETFWG.objects.filter(email_archive__startswith='http') - -urlpatterns = patterns('django.views.generic.list_detail', - (r'^wg/$', 'object_list', { 'queryset': http_archive_wg_queryset, 'template_name': 'mailinglists/wgwebmail_list.html' }), -) -urlpatterns += patterns('', +urlpatterns = patterns('', + (r'^wg/$', 'ietf.mailinglists.views.groups'), (r'^nonwg/$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/list/nonwg.html'}), (r'^nonwg/update/$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/list/nonwg.html'}), (r'^request/$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/list/request.html' }), diff --git a/ietf/mailinglists/views.py b/ietf/mailinglists/views.py index a4b306690..75cb7335e 100644 --- a/ietf/mailinglists/views.py +++ b/ietf/mailinglists/views.py @@ -1,2 +1,12 @@ # Copyright The IETF Trust 2007, All Rights Reserved +from ietf.group.models import Group +from django.shortcuts import render_to_response +from django.template import RequestContext + +def groups(request): + groups = Group.objects.filter(type__in=("wg", "rg"), list_archive__startswith='http').order_by("acronym") + + return render_to_response("mailinglists/group_archives.html", { "groups": groups }, + context_instance=RequestContext(request)) + diff --git a/ietf/templates/mailinglists/wgwebmail_list.html b/ietf/templates/mailinglists/group_archives.html similarity index 57% rename from ietf/templates/mailinglists/wgwebmail_list.html rename to ietf/templates/mailinglists/group_archives.html index 5f1471f9c..54f07377f 100644 --- a/ietf/templates/mailinglists/wgwebmail_list.html +++ b/ietf/templates/mailinglists/group_archives.html @@ -11,21 +11,20 @@ charters for more information about the mailing lists and archives of specific working groups. Charters for active working groups are available on - the Active - IETF Working Groups Web page. Charters for concluded working - groups are available on + the Active IETF Working Groups Web page. + Charters for concluded working groups are available on the Concluded Working Groups Web page.

      -
      {% block intro_prefix %}IPR that was submitted by {{ q }}, and{% endblock %} {% block related %} - {% with ipr.docs as docs %} - {% if not docs %} - is not related to a specific IETF contribution. + {% with ipr.docs as iprdocaliases %} + {% if not iprdocaliases %} + is not related to a specific IETF contribution. {% else %} is related to - {% for item in docs %} - {% if forloop.last and forloop.counter > 1 %}and{% endif %} - {{ item.document }}, "{{ item.document.title|escape }},"{% if item.document.related %}, {{ item.document.relation }} {{ item.document.related }}, "{{ item.document.related.title|escape }}"{% endif %} + {% for item in iprdocaliases %} + {% if forloop.last and forloop.counter > 1 %}and{% endif %} + {{ item.formatted_name|rfcspace }}, "{{ item.doc_alias.document.title }}"{% if not forloop.last and forloop.counter > 1 %},{% endif %} {% endfor %} {% endif %} {% endwith %} - {% endblock %} + {% endblock %} {% block intro_suffix %}{% endblock %}
      {{ ipr.submitted_date }}
    • ID # {{ ipr.ipr_id }}
    • "{{ ipr.title|escape }}""{{ ipr.title }}"
      {{ item.updated.submitted_date }}
    • ID # {{ item.updated.ipr_id }}
    • - IPR disclosure ID# {{ ipr.ipr_id }} "{{ ipr.title|escape }}" - Updates {{ item.updated.title|escape }} + IPR disclosure ID# {{ ipr.ipr_id }} "{{ ipr.title }}" + Updates {{ item.updated.title }}
      - - - -{% for wg in object_list|dictsort:"group_acronym.acronym" %} - - - - -{% endfor %} +
      AcronymName
      {{ wg|escape }}{{ wg.group_acronym.name|escape }}
      + + + + {% for group in groups %} + + + + + {% endfor %}
      AcronymName
      {{ group.acronym }}{{ group.name }}
      {% endblock %} From 8a4990cb1e39c09dc3cea805c447ebd16a6f376e Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Dec 2013 19:00:52 +0000 Subject: [PATCH 134/173] Remove some redundant imports from doc/proxy.py - Legacy-Id: 6783 --- ietf/doc/proxy.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ietf/doc/proxy.py b/ietf/doc/proxy.py index c302bd268..889307495 100644 --- a/ietf/doc/proxy.py +++ b/ietf/doc/proxy.py @@ -1006,7 +1006,6 @@ class IprDraftProxy(IprDocAlias): # document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr") @property def document(self): - from ietf.doc.proxy import DraftLikeDocAlias return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id) #revision = models.CharField(max_length=2) @@ -1026,7 +1025,6 @@ class IprRfcProxy(IprDocAlias): # document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr") @property def document(self): - from ietf.doc.proxy import DraftLikeDocAlias return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id) #revision = models.CharField(max_length=2) From bbcbfc3a059015c32fe1cc3cb3ba7e548cfd444c Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 4 Dec 2013 17:59:25 +0000 Subject: [PATCH 135/173] Drop the obsolete backwards-compatible email.Utils import in ietf_filters, add strip_email filter for getting rid of the email part of a "name " string, fix spelling mistake - Legacy-Id: 6791 --- ietf/doc/templatetags/ietf_filters.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 30d7f792e..deaeeb6bd 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -10,14 +10,12 @@ from django.template import resolve_variable from django.utils.safestring import mark_safe, SafeData from django.utils import simplejson from django.utils.html import strip_tags -try: - from email import utils as emailutils -except ImportError: - from email import Utils as emailutils +from django.template import RequestContext + +import email.utils import re import datetime import types -from django.template import RequestContext register = template.Library() @@ -74,14 +72,23 @@ def parse_email_list(value): addrs = re.split(", ?", value) ret = [] for addr in addrs: - (name, email) = emailutils.parseaddr(addr) + (name, email) = email.utils.parseaddr(addr) if not(name): name = email ret.append('%s' % ( fix_ampersands(email), escape(name) )) return ", ".join(ret) else: return value - + +@register.filter +def strip_email(value): + """Get rid of email part of name/email string like 'Some Name '.""" + if not value: + return "" + if "@" not in value: + return value + return email.utils.parseaddr(value)[0] + @register.filter(name='fix_angle_quotes') def fix_angle_quotes(value): if "<" in value: @@ -93,7 +100,7 @@ def fix_angle_quotes(value): @register.filter(name='make_one_per_line') def make_one_per_line(value): """ - Turn a comma-separated list into a carraige-return-seperated list. + Turn a comma-separated list into a carriage-return-seperated list. >>> make_one_per_line("a, b, c") 'a\\nb\\nc' From 18743a3e96576da27a8164d137ab1464f666f8e1 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 4 Dec 2013 18:25:44 +0000 Subject: [PATCH 136/173] mark_safe result from parse_email_list filter, fix an import bug - Legacy-Id: 6792 --- ietf/doc/templatetags/ietf_filters.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index deaeeb6bd..d35db1c4e 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -12,7 +12,7 @@ from django.utils import simplejson from django.utils.html import strip_tags from django.template import RequestContext -import email.utils +from email.utils import parseaddr import re import datetime import types @@ -72,11 +72,11 @@ def parse_email_list(value): addrs = re.split(", ?", value) ret = [] for addr in addrs: - (name, email) = email.utils.parseaddr(addr) + (name, email) = parseaddr(addr) if not(name): name = email ret.append('%s' % ( fix_ampersands(email), escape(name) )) - return ", ".join(ret) + return mark_safe(", ".join(ret)) else: return value @@ -87,7 +87,7 @@ def strip_email(value): return "" if "@" not in value: return value - return email.utils.parseaddr(value)[0] + return parseaddr(value)[0] @register.filter(name='fix_angle_quotes') def fix_angle_quotes(value): From 97bf521ebb02c60c29c961d9958cc8206a4e6e9b Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 5 Dec 2013 12:59:25 +0000 Subject: [PATCH 137/173] Fix some bugs in the liaisons Javascript, reformulate it slightly to not create jQuery plugins - Legacy-Id: 6793 --- static/js/liaisons.js | 818 +++++++++++++++++++++--------------------- 1 file changed, 404 insertions(+), 414 deletions(-) diff --git a/static/js/liaisons.js b/static/js/liaisons.js index 0a6ebbb46..f6fbd4be4 100644 --- a/static/js/liaisons.js +++ b/static/js/liaisons.js @@ -1,448 +1,438 @@ -(function ($) { - $.fn.AttachmentWidget = function() { - return this.each(function () { - var button = $(this); - var fieldset = $(this).parents('.fieldset'); - var config = {}; - var count = 0; +jQuery(function () { + function setupAttachmentWidget() { + var button = jQuery(this); + var fieldset = jQuery(this).parents('.fieldset'); + var config = {}; + var count = 0; - var readConfig = function() { - var disabledLabel = fieldset.find('.attachDisabledLabel'); + var readConfig = function() { + var disabledLabel = fieldset.find('.attachDisabledLabel'); - if (disabledLabel.length) { - config.disabledLabel = disabledLabel.html(); - var required = '' - fieldset.find('.attachRequiredField').each(function(index, field) { - required += '#' + $(field).html() + ','; - }); - var fields = fieldset.find(required); - config.basefields = fields; - } - - config.showOn = $('#' + fieldset.find('.showAttachsOn').html()); - config.showOnDisplay = config.showOn.find('.attachedFiles'); - count = config.showOnDisplay.find('.initialAttach').length; - config.showOnEmpty = config.showOn.find('.showAttachmentsEmpty').html(); - config.enabledLabel = fieldset.find('.attachEnabledLabel').html(); - }; - - var setState = function() { - var enabled = true; - config.fields.each(function() { - if (!$(this).val()) { - enabled = false; - return; - } + if (disabledLabel.length) { + config.disabledLabel = disabledLabel.html(); + var required = ''; + fieldset.find('.attachRequiredField').each(function(index, field) { + required += '#' + jQuery(field).html() + ','; }); - if (enabled) { - button.removeAttr('disabled').removeClass('disabledAddAttachment'); - button.val(config.enabledLabel); - } else { - button.attr('disabled', 'disabled').addClass('disabledAddAttachment'); - button.val(config.disabledLabel); - } - }; - - var cloneFields = function() { - var html = '
      '; - if (count) { - html = config.showOnDisplay.html() + html; - } - config.fields.each(function() { - var field = $(this); - var container= $(this).parents('.field'); - if (container.find(':file').length) { - html += ' (' + field.val() + ')'; - } else { - html += ' ' + field.val(); - } - html += ''; - container.hide(); - }); - html += ' Remove'; - html += '
      '; - config.showOnDisplay.html(html); - count += 1; - initFileInput(); - }; - - var doAttach = function() { - cloneFields(); - setState(); - }; - - var removeAttachment = function() { - var link = $(this); - var attach = $(this).parent('.attachedFileInfo'); - var fields = attach.find('.removeField'); - fields.each(function() { - $('#' + $(this).html()).remove(); - }); - attach.remove(); - if (!config.showOnDisplay.html()) { - config.showOnDisplay.html(config.showOnEmpty); - count = 0; - } - return false; - }; - - var initTriggers = function() { - config.showOnDisplay.find('a.removeAttach').live('click', removeAttachment); - button.click(doAttach); - }; - - var initFileInput = function() { - var fieldids = '' - config.basefields.each(function(i) { - var field = $(this); - var oldcontainer= $(this).parents('.field'); - var newcontainer= oldcontainer.clone(); - var newfield = newcontainer.find('#' + field.attr('id')); - newfield.attr('name', newfield.attr('name') + '_' + count); - newfield.attr('id', newfield.attr('id') + '_' + count); - newcontainer.attr('id', 'container_id_' + newfield.attr('name')); - oldcontainer.after(newcontainer); - oldcontainer.hide(); - newcontainer.show(); - fieldids += '#' + newfield.attr('id') + ',' - }); - config.fields = $(fieldids); - config.fields.change(setState); - config.fields.keyup(setState); - }; - - var initWidget = function() { - readConfig(); - initFileInput(); - initTriggers(); - setState(); - }; - - initWidget(); - }); - }; - - $.fn.LiaisonForm = function() { - return this.each(function () { - var form = $(this); - var organization = form.find('#id_organization'); - var from = form.find('#id_from_field'); - var poc = form.find('#id_to_poc'); - var cc = form.find('#id_cc1'); - var reply = form.find('#id_replyto'); - var purpose = form.find('#id_purpose'); - var other_purpose = form.find('#id_purpose_text'); - var deadline = form.find('#id_deadline_date'); - var submission_date = form.find('#id_submitted_date'); - var other_organization = form.find('#id_other_organization'); - var approval = form.find('#id_approved'); - var cancel = form.find('#id_cancel'); - var cancel_dialog = form.find('#cancel-dialog'); - var config = {}; - var related_trigger = form.find('#id_related_to'); - var related_url = form.find('#id_related_to').parent().find('.listURL').text(); - var related_dialog = form.find('#related-dialog'); - var unrelate_trigger = form.find('#id_no_related_to'); - - var readConfig = function() { - var confcontainer = form.find('.formconfig'); - config.info_update_url = confcontainer.find('.info_update_url').text(); - }; - - var render_mails_into = function(container, person_list, as_html) { - var html=''; - - $.each(person_list, function(index, person) { - if (as_html) { - html += person[0] + ' <'+person[1]+'>
      '; - } else { - html += person[0] + ' <'+person[1]+'>\n'; - } - }); - container.html(html); - }; - - var toggleApproval = function(needed) { - if (!approval.length) { - return; - } - if (needed) { - approval.removeAttr('disabled'); - approval.removeAttr('checked'); - } else { - approval.attr('checked','checked'); - approval.attr('disabled','disabled'); - } - }; - - var checkPostOnly = function(post_only) { - if (post_only) { - $("input[name=send]").hide(); - } else { - $("input[name=send]").show(); - } - }; - - var updateReplyTo = function() { - var select = form.find('select[name=from_fake_user]'); - var option = select.find('option:selected'); - reply.val(option.attr('title')); - updateFrom(); + var fields = fieldset.find(required); + config.basefields = fields; } - var userSelect = function(user_list) { - if (!user_list || !user_list.length) { + config.showOn = jQuery('#' + fieldset.find('.showAttachsOn').html()); + config.showOnDisplay = config.showOn.find('.attachedFiles'); + count = config.showOnDisplay.find('.initialAttach').length; + config.showOnEmpty = config.showOn.find('.showAttachmentsEmpty').html(); + config.enabledLabel = fieldset.find('.attachEnabledLabel').html(); + }; + + var setState = function() { + var enabled = true; + config.fields.each(function() { + if (!jQuery(this).val()) { + enabled = false; return; } - var link = form.find('a.from_mailto'); - var select = form.find('select[name=from_fake_user]'); - var options = ''; - link.hide(); - $.each(user_list, function(index, person) { - options += ''; - }); - select.remove(); - link.after('') - form.find('select[name=from_fake_user]').change(updateReplyTo); - updateReplyTo(); - }; + }); + if (enabled) { + button.removeAttr('disabled').removeClass('disabledAddAttachment'); + button.val(config.enabledLabel); + } else { + button.attr('disabled', 'disabled').addClass('disabledAddAttachment'); + button.val(config.disabledLabel); + } + }; - var updateInfo = function(first_time, sender) { - var entity = organization; - var to_entity = from; - if (!entity.is('select') || !to_entity.is('select')) { - return false; + var cloneFields = function() { + var html = '
      '; + if (count) { + html = config.showOnDisplay.html() + html; + } + config.fields.each(function() { + var field = jQuery(this); + var container= jQuery(this).parents('.field'); + if (container.find(':file').length) { + html += ' (' + field.val() + ')'; + } else { + html += ' ' + field.val(); } - var url = config.info_update_url; - $.ajax({ - url: url, - type: 'GET', - cache: false, - async: true, - dataType: 'json', - data: {to_entity_id: organization.val(), - from_entity_id: to_entity.val()}, - success: function(response){ - if (!response.error) { - if (!first_time || !cc.text()) { - render_mails_into(cc, response.cc, false); - } - render_mails_into(poc, response.poc, true); - toggleApproval(response.needs_approval); - checkPostOnly(response.post_only); - if (sender == 'from') { - userSelect(response.full_list); - } + html += ''; + container.hide(); + }); + html += ' Remove'; + html += '
      '; + config.showOnDisplay.html(html); + count += 1; + initFileInput(); + }; + + var doAttach = function() { + cloneFields(); + setState(); + }; + + var removeAttachment = function() { + var link = jQuery(this); + var attach = jQuery(this).parent('.attachedFileInfo'); + var fields = attach.find('.removeField'); + fields.each(function() { + jQuery('#' + jQuery(this).html()).remove(); + }); + attach.remove(); + if (!config.showOnDisplay.html()) { + config.showOnDisplay.html(config.showOnEmpty); + count = 0; + } + return false; + }; + + var initTriggers = function() { + config.showOnDisplay.find('a.removeAttach').live('click', removeAttachment); + button.click(doAttach); + }; + + var initFileInput = function() { + var fieldids = []; + config.basefields.each(function(i) { + var field = jQuery(this); + var oldcontainer= jQuery(this).parents('.field'); + var newcontainer= oldcontainer.clone(); + var newfield = newcontainer.find('#' + field.attr('id')); + newfield.attr('name', newfield.attr('name') + '_' + count); + newfield.attr('id', newfield.attr('id') + '_' + count); + newcontainer.attr('id', 'container_id_' + newfield.attr('name')); + oldcontainer.after(newcontainer); + oldcontainer.hide(); + newcontainer.show(); + fieldids.push('#' + newfield.attr('id')); + }); + config.fields = jQuery(fieldids.join(",")); + config.fields.change(setState); + config.fields.keyup(setState); + }; + + var initWidget = function() { + readConfig(); + initFileInput(); + initTriggers(); + setState(); + }; + + initWidget(); + } + + jQuery('form.liaisonform').each(function() { + var form = jQuery(this); + var organization = form.find('#id_organization'); + var from = form.find('#id_from_field'); + var poc = form.find('#id_to_poc'); + var cc = form.find('#id_cc1'); + var reply = form.find('#id_replyto'); + var purpose = form.find('#id_purpose'); + var other_purpose = form.find('#id_purpose_text'); + var deadline = form.find('#id_deadline_date'); + var submission_date = form.find('#id_submitted_date'); + var other_organization = form.find('#id_other_organization'); + var approval = form.find('#id_approved'); + var cancel = form.find('#id_cancel'); + var cancel_dialog = form.find('#cancel-dialog'); + var config = {}; + var related_trigger = form.find('#id_related_to'); + var related_url = form.find('#id_related_to').parent().find('.listURL').text(); + var related_dialog = form.find('#related-dialog'); + var unrelate_trigger = form.find('#id_no_related_to'); + + var readConfig = function() { + var confcontainer = form.find('.formconfig'); + config.info_update_url = confcontainer.find('.info_update_url').text(); + }; + + var render_mails_into = function(container, person_list, as_html) { + var html=''; + + jQuery.each(person_list, function(index, person) { + if (as_html) { + html += person[0] + ' <'+person[1]+'>
      '; + } else { + html += person[0] + ' <'+person[1]+'>\n'; + } + }); + container.html(html); + }; + + var toggleApproval = function(needed) { + if (!approval.length) { + return; + } + if (needed) { + approval.removeAttr('disabled'); + approval.removeAttr('checked'); + } else { + approval.attr('checked','checked'); + approval.attr('disabled','disabled'); + } + }; + + var checkPostOnly = function(post_only) { + if (post_only) { + jQuery("input[name=send]").hide(); + } else { + jQuery("input[name=send]").show(); + } + }; + + var updateReplyTo = function() { + var select = form.find('select[name=from_fake_user]'); + var option = select.find('option:selected'); + reply.val(option.attr('title')); + updateFrom(); + }; + + var userSelect = function(user_list) { + if (!user_list || !user_list.length) { + return; + } + var link = form.find('a.from_mailto'); + var select = form.find('select[name=from_fake_user]'); + var options = ''; + link.hide(); + jQuery.each(user_list, function(index, person) { + options += ''; + }); + select.remove(); + link.after(''); + form.find('select[name=from_fake_user]').change(updateReplyTo); + updateReplyTo(); + }; + + var updateInfo = function(first_time, sender) { + var entity = organization; + var to_entity = from; + if (!entity.is('select') || !to_entity.is('select')) { + return false; + } + var url = config.info_update_url; + jQuery.ajax({ + url: url, + type: 'GET', + cache: false, + async: true, + dataType: 'json', + data: {to_entity_id: organization.val(), + from_entity_id: to_entity.val()}, + success: function(response){ + if (!response.error) { + if (!first_time || !cc.text()) { + render_mails_into(cc, response.cc, false); + } + render_mails_into(poc, response.poc, true); + toggleApproval(response.needs_approval); + checkPostOnly(response.post_only); + if (sender == 'from') { + userSelect(response.full_list); } } - }); - return false; - }; - - var updateFrom = function() { - var reply_to = reply.val(); - form.find('a.from_mailto').attr('href', 'mailto:' + reply_to); - }; - - var updatePurpose = function() { - var datecontainer = deadline.parents('.field'); - var othercontainer = other_purpose.parents('.field'); - var selected_id = purpose.val(); - var deadline_required = datecontainer.find('.fieldRequired'); - - if (selected_id == '1' || selected_id == '2' || selected_id == '5') { - datecontainer.show(); - } else { - datecontainer.hide(); - deadline.val(''); } + }); + return false; + }; - if (selected_id == '5') { - othercontainer.show(); - deadline_required.hide(); - } else { - othercontainer.hide(); - other_purpose.val(''); - deadline_required.show(); - } - }; + var updateFrom = function() { + var reply_to = reply.val(); + form.find('a.from_mailto').attr('href', 'mailto:' + reply_to); + }; - var checkOtherSDO = function() { - var entity = organization.val(); - if (entity=='othersdo') { - other_organization.parents('.field').show(); - } else { - other_organization.parents('.field').hide(); - } - }; + var updatePurpose = function() { + var datecontainer = deadline.parents('.field'); + var othercontainer = other_purpose.parents('.field'); + var selected_id = purpose.val(); + var deadline_required = datecontainer.find('.fieldRequired'); - var cancelForm = function() { - cancel_dialog.dialog("open"); - }; + if (selected_id == '1' || selected_id == '2' || selected_id == '5') { + datecontainer.show(); + } else { + datecontainer.hide(); + deadline.val(''); + } - var getRelatedLink = function() { - link = $(this).text();; - pk = $(this).nextAll('.liaisonPK').text(); - widget = related_trigger.parent(); - widget.find('.relatedLiaisonWidgetTitle').text(link); - widget.find('.relatedLiaisonWidgetValue').val(pk); - widget.find('.noRelated').hide(); - unrelate_trigger.show(); - related_dialog.dialog('close'); - return false; - }; + if (selected_id == '5') { + othercontainer.show(); + deadline_required.hide(); + } else { + othercontainer.hide(); + other_purpose.val(''); + deadline_required.show(); + } + }; - var selectNoRelated = function() { - widget = $(this).parent(); - widget.find('.relatedLiaisonWidgetTitle').text(''); - widget.find('.noRelated').show(); - widget.find('.relatedLiaisonWidgetValue').val(''); - $(this).hide(); - return false; - }; + var checkOtherSDO = function() { + var entity = organization.val(); + if (entity=='othersdo') { + other_organization.parents('.field').show(); + } else { + other_organization.parents('.field').hide(); + } + }; - var selectRelated = function() { - trigger = $(this); - widget = $(this).parent(); - url = widget.find('.listURL').text(); - title = widget.find('.relatedLiaisonWidgetTitle'); - related_dialog.html(''); - related_dialog.dialog('open'); - $.ajax({ - url: url, - type: 'GET', - cache: false, - async: true, - dataType: 'html', - success: function(response){ - related_dialog.html(response); - related_dialog.find('th a').click(function() { - widget.find('.listURL').text(related_url + $(this).attr('href')); - trigger.click(); - return false; - }); - related_dialog.find('td a').click(getRelatedLink); - } - }); - return false; - }; + var cancelForm = function() { + cancel_dialog.dialog("open"); + }; - var checkFrom = function(first_time) { - var reduce_options = form.find('.reducedToOptions'); - if (!reduce_options.length) { - updateInfo(first_time, 'from'); - return; - } - var to_select = organization; - var from_entity = from.val(); - if (!reduce_options.find('.full_power_on_' + from_entity).length) { - to_select.find('optgroup').eq(1).hide(); - to_select.find('option').each(function() { - if (!reduce_options.find('.reduced_to_set_' + $(this).val()).length) { - $(this).hide(); - } else { - $(this).show(); - } + var getRelatedLink = function() { + var link = jQuery(this).text();; + var pk = jQuery(this).nextAll('.liaisonPK').text(); + var widget = related_trigger.parent(); + widget.find('.relatedLiaisonWidgetTitle').text(link); + widget.find('.relatedLiaisonWidgetValue').val(pk); + widget.find('.noRelated').hide(); + unrelate_trigger.show(); + related_dialog.dialog('close'); + return false; + }; + + var selectNoRelated = function() { + var widget = jQuery(this).parent(); + widget.find('.relatedLiaisonWidgetTitle').text(''); + widget.find('.noRelated').show(); + widget.find('.relatedLiaisonWidgetValue').val(''); + jQuery(this).hide(); + return false; + }; + + var selectRelated = function() { + var trigger = jQuery(this); + var widget = jQuery(this).parent(); + var url = widget.find('.listURL').text(); + var title = widget.find('.relatedLiaisonWidgetTitle'); + related_dialog.html(''); + related_dialog.dialog('open'); + jQuery.ajax({ + url: url, + type: 'GET', + cache: false, + async: true, + dataType: 'html', + success: function(response){ + related_dialog.html(response); + related_dialog.find('th a').click(function() { + widget.find('.listURL').text(related_url + jQuery(this).attr('href')); + trigger.click(); + return false; }); - if (!to_select.find('option:selected').is(':visible')) { - to_select.find('option:selected').removeAttr('selected'); - } - } else { - to_select.find('optgroup').show(); - to_select.find('option').show(); + related_dialog.find('td a').click(getRelatedLink); } + }); + return false; + }; + + var checkFrom = function(first_time) { + var reduce_options = form.find('.reducedToOptions'); + if (!reduce_options.length) { updateInfo(first_time, 'from'); - }; - - var checkSubmissionDate = function() { - var date_str = submission_date.val(); - if (date_str) { - var sdate = new Date(date_str); - var today = new Date(); - if (Math.abs(today-sdate) > 2592000000) { // 2592000000 = 30 days in milliseconds - return confirm('Submission date ' + date_str + ' differ more than 30 days.\n\nDo you want to continue and post this liaison using that submission date?\n'); + return; + } + var to_select = organization; + var from_entity = from.val(); + if (!reduce_options.find('.full_power_on_' + from_entity).length) { + to_select.find('optgroup').eq(1).hide(); + to_select.find('option').each(function() { + if (!reduce_options.find('.reduced_to_set_' + jQuery(this).val()).length) { + jQuery(this).hide(); + } else { + jQuery(this).show(); } + }); + if (!to_select.find('option:selected').is(':visible')) { + to_select.find('option:selected').removeAttr('selected'); } - }; + } else { + to_select.find('optgroup').show(); + to_select.find('option').show(); + } + updateInfo(first_time, 'from'); + }; - var initTriggers = function() { - organization.change(function() {updateInfo(false, 'to');}); - organization.change(checkOtherSDO); - from.change(function() {checkFrom(false);}); - reply.keyup(updateFrom); - purpose.change(updatePurpose); - cancel.click(cancelForm); - related_trigger.click(selectRelated); - unrelate_trigger.click(selectNoRelated); - form.submit(checkSubmissionDate); - }; + var checkSubmissionDate = function() { + var date_str = submission_date.val(); + if (date_str) { + var sdate = new Date(date_str); + var today = new Date(); + if (Math.abs(today-sdate) > 2592000000) { // 2592000000 = 30 days in milliseconds + return confirm('Submission date ' + date_str + ' differ more than 30 days.\n\nDo you want to continue and post this liaison using that submission date?\n'); + } + } + }; - var updateOnInit = function() { - updateFrom(); - checkFrom(true); - updatePurpose(); - checkOtherSDO(); - }; + var initTriggers = function() { + organization.change(function() {updateInfo(false, 'to');}); + organization.change(checkOtherSDO); + from.change(function() {checkFrom(false);}); + reply.keyup(updateFrom); + purpose.change(updatePurpose); + cancel.click(cancelForm); + related_trigger.click(selectRelated); + unrelate_trigger.click(selectNoRelated); + form.submit(checkSubmissionDate); + }; - var initDatePicker = function() { - deadline.datepicker({ - dateFormat: $.datepicker.ATOM, - changeYear: true - }); - submission_date.datepicker({ - dateFormat: $.datepicker.ATOM, - changeYear: true - }); - }; + var updateOnInit = function() { + updateFrom(); + checkFrom(true); + updatePurpose(); + checkOtherSDO(); + }; - var initAttachments = function() { - form.find('.addAttachmentWidget').AttachmentWidget(); - }; + var initDatePicker = function() { + deadline.datepicker({ + dateFormat: jQuery.datepicker.ATOM, + changeYear: true + }); + submission_date.datepicker({ + dateFormat: jQuery.datepicker.ATOM, + changeYear: true + }); + }; - var initDialogs = function() { - cancel_dialog.dialog({ - resizable: false, - height:200, - modal: true, - autoOpen: false, - buttons: { - Ok: function() { - window.location='..'; - $( this ).dialog( "close" ); - }, - Cancel: function() { - $( this ).dialog( "close" ); - } - } - }); + var initAttachments = function() { + form.find('.addAttachmentWidget').each(setupAttachmentWidget); + }; - related_dialog.dialog({ - height: 400, - width: 800, - draggable: true, - modal: true, - autoOpen: false - }); - }; + var initDialogs = function() { + cancel_dialog.dialog({ + resizable: false, + height:200, + modal: true, + autoOpen: false, + buttons: { + "Ok": function() { + window.location='..'; + jQuery( this ).dialog( "close" ); + }, + "Cancel": function() { + jQuery( this ).dialog( "close" ); + } + } + }); - var initForm = function() { - readConfig(); - initTriggers(); - updateOnInit(); - initDatePicker(); - initAttachments(); - initDialogs(); - }; + related_dialog.dialog({ + height: 400, + width: 800, + draggable: true, + modal: true, + autoOpen: false + }); + }; - initForm(); - }); + var initForm = function() { + readConfig(); + initTriggers(); + updateOnInit(); + initDatePicker(); + initAttachments(); + initDialogs(); + }; - }; - - $(document).ready(function () { - $('form.liaisonform').LiaisonForm(); + initForm(); }); - -})(jQuery); +}); From 4250a95556dbd42cb691b67ffaf9339b370ef093 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 5 Dec 2013 13:54:48 +0000 Subject: [PATCH 138/173] Remove proxy layer from liaisons tool, do some minor cleanups of duplicated code, make sure the custom form widgets escape their input. There's still a bit of custom proxying going on in the IETFHM class hierarchy. - Legacy-Id: 6794 --- ietf/liaisons/__init__.py | 2 +- ietf/liaisons/accounts.py | 114 ++-- ietf/liaisons/accountsREDESIGN.py | 140 ----- ietf/liaisons/admin.py | 7 - ietf/liaisons/decorators.py | 5 - ietf/liaisons/feeds.py | 113 ++-- ietf/liaisons/forms.py | 229 ++++---- ietf/liaisons/formsREDESIGN.py | 474 ---------------- ietf/liaisons/mails.py | 11 +- .../commands/check_liaison_deadlines.py | 75 +-- .../commands/remind_update_sdo_list.py | 48 +- ietf/liaisons/models.py | 391 ++----------- ietf/liaisons/proxy.py | 188 ------- ietf/liaisons/sitemaps.py | 13 +- ietf/liaisons/tests.py | 250 +++++---- ietf/liaisons/testurl.list | 33 -- ietf/liaisons/urls.py | 11 +- ietf/liaisons/utils.py | 233 +++++--- ietf/liaisons/utilsREDESIGN.py | 524 ------------------ ietf/liaisons/views.py | 271 +++------ ietf/liaisons/widgets.py | 59 +- ietf/templates/liaisons/approval_detail.html | 10 + ..._approval_list.html => approval_list.html} | 2 +- ietf/templates/liaisons/detail.html | 120 ++++ ietf/templates/liaisons/edit.html | 32 ++ ietf/templates/liaisons/liaison_mail.txt | 20 +- .../liaisons/liaison_mail_detail.html | 31 -- .../liaisons/liaison_main_management.html | 62 --- ietf/templates/liaisons/liaison_table.html | 39 ++ ietf/templates/liaisons/liaison_title.html | 6 +- .../liaisondetail_approval_detail.html | 10 - .../liaisons/liaisondetail_detail.html | 110 ---- .../liaisons/liaisondetail_edit.html | 31 -- .../liaisons/liaisondetail_simple_list.html | 51 -- ...{liaisondetail_list.html => overview.html} | 4 +- static/css/liaisons.css | 29 +- 36 files changed, 940 insertions(+), 2808 deletions(-) delete mode 100644 ietf/liaisons/accountsREDESIGN.py delete mode 100644 ietf/liaisons/decorators.py delete mode 100644 ietf/liaisons/formsREDESIGN.py delete mode 100644 ietf/liaisons/proxy.py delete mode 100644 ietf/liaisons/testurl.list delete mode 100644 ietf/liaisons/utilsREDESIGN.py create mode 100644 ietf/templates/liaisons/approval_detail.html rename ietf/templates/liaisons/{liaisondetail_approval_list.html => approval_list.html} (86%) create mode 100644 ietf/templates/liaisons/detail.html create mode 100644 ietf/templates/liaisons/edit.html delete mode 100644 ietf/templates/liaisons/liaison_mail_detail.html delete mode 100644 ietf/templates/liaisons/liaison_main_management.html create mode 100644 ietf/templates/liaisons/liaison_table.html delete mode 100644 ietf/templates/liaisons/liaisondetail_approval_detail.html delete mode 100644 ietf/templates/liaisons/liaisondetail_detail.html delete mode 100644 ietf/templates/liaisons/liaisondetail_edit.html delete mode 100644 ietf/templates/liaisons/liaisondetail_simple_list.html rename ietf/templates/liaisons/{liaisondetail_list.html => overview.html} (72%) diff --git a/ietf/liaisons/__init__.py b/ietf/liaisons/__init__.py index 3f64c6e2b..aeccae32c 100644 --- a/ietf/liaisons/__init__.py +++ b/ietf/liaisons/__init__.py @@ -2,7 +2,7 @@ # coding: latin-1 from types import ModuleType -import urls, models, views, forms, accounts, admin, utils, widgets, decorators, sitemaps, feeds +import urls, models, views, forms, admin, utils, widgets, sitemaps, feeds # These people will be sent a stack trace if there's an uncaught exception in # code any of the modules imported above: diff --git a/ietf/liaisons/accounts.py b/ietf/liaisons/accounts.py index 144c0bd53..fc3fffa07 100644 --- a/ietf/liaisons/accounts.py +++ b/ietf/liaisons/accounts.py @@ -1,14 +1,15 @@ -from django.conf import settings +from ietf.person.models import Person +from ietf.group.models import Role +from ietf.utils.proxy import proxy_personify_role -from ietf.idtracker.models import Role, PersonOrOrgInfo - - -LIAISON_EDIT_GROUPS = ['Secretariat'] +LIAISON_EDIT_GROUPS = ['Secretariat'] # this is not working anymore, refers to old auth model def get_ietf_chair(): - person = PersonOrOrgInfo.objects.filter(role=Role.IETF_CHAIR) - return person and person[0] or None + try: + return proxy_personify_role(Role.objects.get(name="chair", group__acronym="ietf")) + except Role.DoesNotExist: + return None def get_iesg_chair(): @@ -16,48 +17,62 @@ def get_iesg_chair(): def get_iab_chair(): - person = PersonOrOrgInfo.objects.filter(role=Role.IAB_CHAIR) - return person and person[0] or None - - -def get_iab_executive_director(): - person = PersonOrOrgInfo.objects.filter(role=Role.IAB_EXCUTIVE_DIRECTOR) - return person and person[0] or None - - -def get_person_for_user(user): try: - return user.get_profile().person() - except: + return proxy_personify_role(Role.objects.get(name="chair", group__acronym="iab")) + except Role.DoesNotExist: return None +def get_irtf_chair(): + try: + return proxy_personify_role(Role.objects.get(name="chair", group__acronym="irtf")) + except Role.DoesNotExist: + return None + + +def get_iab_executive_director(): + try: + return proxy_personify_role(Role.objects.get(name="execdir", group__acronym="iab")) + except Person.DoesNotExist: + return None + + +def get_person_for_user(user): + if not user.is_authenticated(): + return None + try: + p = user.get_profile() + p.email = lambda: (p.plain_name(), p.email_address()) + return p + except Person.DoesNotExist: + return None + def is_areadirector(person): - return bool(person.areadirector_set.all()) + return bool(Role.objects.filter(person=person, name="ad", group__state="active", group__type="area")) def is_wgchair(person): - return bool(person.wgchair_set.all()) + return bool(Role.objects.filter(person=person, name="chair", group__state="active", group__type="wg")) def is_wgsecretary(person): - return bool(person.wgsecretary_set.all()) - - -def has_role(person, role): - return bool(person.role_set.filter(pk=role)) + return bool(Role.objects.filter(person=person, name="sec", group__state="active", group__type="wg")) def is_ietfchair(person): - return has_role(person, Role.IETF_CHAIR) + return bool(Role.objects.filter(person=person, name="chair", group__acronym="ietf")) def is_iabchair(person): - return has_role(person, Role.IAB_CHAIR) + return bool(Role.objects.filter(person=person, name="chair", group__acronym="iab")) def is_iab_executive_director(person): - return has_role(person, Role.IAB_EXCUTIVE_DIRECTOR) + return bool(Role.objects.filter(person=person, name="execdir", group__acronym="iab")) + + +def is_irtfchair(person): + return bool(Role.objects.filter(person=person, name="chair", group__acronym="irtf")) def can_add_outgoing_liaison(user): @@ -74,15 +89,17 @@ def can_add_outgoing_liaison(user): def is_sdo_liaison_manager(person): - return bool(person.liaisonmanagers_set.all()) + return bool(Role.objects.filter(person=person, name="liaiman", group__type="sdo")) def is_sdo_authorized_individual(person): - return bool(person.sdoauthorizedindividual_set.all()) + return bool(Role.objects.filter(person=person, name="auth", group__type="sdo")) def is_secretariat(user): - return bool(user.groups.filter(name='Secretariat')) + if isinstance(user, basestring): + return False + return user.is_authenticated() and bool(Role.objects.filter(person__user=user, name="secr", group__acronym="secretariat")) def can_add_incoming_liaison(user): @@ -102,36 +119,14 @@ def can_add_liaison(user): def is_sdo_manager_for_outgoing_liaison(person, liaison): - from ietf.liaisons.utils import IETFHM, SDOEntity - from ietf.liaisons.models import SDOs - from_entity = IETFHM.get_entity_by_key(liaison.from_raw_code) - sdo = None - if not from_entity: - try: - sdo = SDOs.objects.get(sdo_name=liaison.from_body()) - except SDOs.DoesNotExist: - pass - elif isinstance(from_entity, SDOEntity): - sdo = from_entity.obj - if sdo: - return bool(sdo.liaisonmanagers_set.filter(person=person)) + if liaison.from_group and liaison.from_group.type_id == "sdo": + return bool(liaison.from_group.role_set.filter(name="liaiman", person=person)) return False def is_sdo_manager_for_incoming_liaison(person, liaison): - from ietf.liaisons.utils import IETFHM, SDOEntity - from ietf.liaisons.models import SDOs - to_entity = IETFHM.get_entity_by_key(liaison.to_raw_code) - sdo = None - if not to_entity: - try: - sdo = SDOs.objects.get(sdo_name=liaison.to_body) - except SDOs.DoesNotExist: - pass - elif isinstance(to_entity, SDOEntity): - sdo = to_entity.obj - if sdo: - return bool(sdo.liaisonmanagers_set.filter(person=person)) + if liaison.to_group and liaison.to_group.type_id == "sdo": + return bool(liaison.to_group.role_set.filter(name="liaiman", person=person)) return False @@ -143,6 +138,3 @@ def can_edit_liaison(user, liaison): return (is_sdo_manager_for_outgoing_liaison(person, liaison) or is_sdo_manager_for_incoming_liaison(person, liaison)) return False - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from accountsREDESIGN import * diff --git a/ietf/liaisons/accountsREDESIGN.py b/ietf/liaisons/accountsREDESIGN.py deleted file mode 100644 index fc3fffa07..000000000 --- a/ietf/liaisons/accountsREDESIGN.py +++ /dev/null @@ -1,140 +0,0 @@ -from ietf.person.models import Person -from ietf.group.models import Role -from ietf.utils.proxy import proxy_personify_role - -LIAISON_EDIT_GROUPS = ['Secretariat'] # this is not working anymore, refers to old auth model - - -def get_ietf_chair(): - try: - return proxy_personify_role(Role.objects.get(name="chair", group__acronym="ietf")) - except Role.DoesNotExist: - return None - - -def get_iesg_chair(): - return get_ietf_chair() - - -def get_iab_chair(): - try: - return proxy_personify_role(Role.objects.get(name="chair", group__acronym="iab")) - except Role.DoesNotExist: - return None - - -def get_irtf_chair(): - try: - return proxy_personify_role(Role.objects.get(name="chair", group__acronym="irtf")) - except Role.DoesNotExist: - return None - - -def get_iab_executive_director(): - try: - return proxy_personify_role(Role.objects.get(name="execdir", group__acronym="iab")) - except Person.DoesNotExist: - return None - - -def get_person_for_user(user): - if not user.is_authenticated(): - return None - try: - p = user.get_profile() - p.email = lambda: (p.plain_name(), p.email_address()) - return p - except Person.DoesNotExist: - return None - -def is_areadirector(person): - return bool(Role.objects.filter(person=person, name="ad", group__state="active", group__type="area")) - - -def is_wgchair(person): - return bool(Role.objects.filter(person=person, name="chair", group__state="active", group__type="wg")) - - -def is_wgsecretary(person): - return bool(Role.objects.filter(person=person, name="sec", group__state="active", group__type="wg")) - - -def is_ietfchair(person): - return bool(Role.objects.filter(person=person, name="chair", group__acronym="ietf")) - - -def is_iabchair(person): - return bool(Role.objects.filter(person=person, name="chair", group__acronym="iab")) - - -def is_iab_executive_director(person): - return bool(Role.objects.filter(person=person, name="execdir", group__acronym="iab")) - - -def is_irtfchair(person): - return bool(Role.objects.filter(person=person, name="chair", group__acronym="irtf")) - - -def can_add_outgoing_liaison(user): - person = get_person_for_user(user) - if not person: - return False - - if (is_areadirector(person) or is_wgchair(person) or - is_wgsecretary(person) or is_ietfchair(person) or - is_iabchair(person) or is_iab_executive_director(person) or - is_sdo_liaison_manager(person) or is_secretariat(user)): - return True - return False - - -def is_sdo_liaison_manager(person): - return bool(Role.objects.filter(person=person, name="liaiman", group__type="sdo")) - - -def is_sdo_authorized_individual(person): - return bool(Role.objects.filter(person=person, name="auth", group__type="sdo")) - - -def is_secretariat(user): - if isinstance(user, basestring): - return False - return user.is_authenticated() and bool(Role.objects.filter(person__user=user, name="secr", group__acronym="secretariat")) - - -def can_add_incoming_liaison(user): - person = get_person_for_user(user) - if not person: - return False - - if (is_sdo_liaison_manager(person) or - is_sdo_authorized_individual(person) or - is_secretariat(user)): - return True - return False - - -def can_add_liaison(user): - return can_add_incoming_liaison(user) or can_add_outgoing_liaison(user) - - -def is_sdo_manager_for_outgoing_liaison(person, liaison): - if liaison.from_group and liaison.from_group.type_id == "sdo": - return bool(liaison.from_group.role_set.filter(name="liaiman", person=person)) - return False - - -def is_sdo_manager_for_incoming_liaison(person, liaison): - if liaison.to_group and liaison.to_group.type_id == "sdo": - return bool(liaison.to_group.role_set.filter(name="liaiman", person=person)) - return False - - -def can_edit_liaison(user, liaison): - if is_secretariat(user): - return True - person = get_person_for_user(user) - if is_sdo_liaison_manager(person): - return (is_sdo_manager_for_outgoing_liaison(person, liaison) or - is_sdo_manager_for_incoming_liaison(person, liaison)) - return False diff --git a/ietf/liaisons/admin.py b/ietf/liaisons/admin.py index f34ea433b..21385011e 100644 --- a/ietf/liaisons/admin.py +++ b/ietf/liaisons/admin.py @@ -8,10 +8,3 @@ class LiaisonStatementAdmin(admin.ModelAdmin): ordering = ('title', ) raw_id_fields = ('from_contact', 'related_to', 'from_group', 'to_group', 'attachments') admin.site.register(LiaisonStatement, LiaisonStatementAdmin) - -class LiaisonDetailAdmin(admin.ModelAdmin): - list_display = ['pk', 'title', 'from_id', 'to_body', 'submitted_date', 'purpose', 'related_to' ] - list_display_links = ['pk', 'title'] - ordering = ('title', ) -admin.site.register(LiaisonDetail, LiaisonDetailAdmin) - \ No newline at end of file diff --git a/ietf/liaisons/decorators.py b/ietf/liaisons/decorators.py deleted file mode 100644 index 744082211..000000000 --- a/ietf/liaisons/decorators.py +++ /dev/null @@ -1,5 +0,0 @@ -from ietf.ietfauth.decorators import passes_test_decorator -from ietf.liaisons.accounts import can_add_liaison - -can_submit_liaison = passes_test_decorator(lambda u, *args, **kwargs: can_add_liaison(u), - "Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities") diff --git a/ietf/liaisons/feeds.py b/ietf/liaisons/feeds.py index 5b5e38716..4e62bb5cb 100644 --- a/ietf/liaisons/feeds.py +++ b/ietf/liaisons/feeds.py @@ -1,24 +1,22 @@ # Copyright The IETF Trust 2007, All Rights Reserved +import re, datetime + from django.conf import settings from django.contrib.syndication.feeds import Feed, FeedDoesNotExist from django.utils.feedgenerator import Atom1Feed from django.db.models import Q -from ietf.liaisons.models import LiaisonDetail, FromBodies -from ietf.idtracker.models import Acronym -from datetime import datetime, time -import re +from django.core.urlresolvers import reverse as urlreverse -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.group.models import Group - from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail - from ietf.liaisons.models import LiaisonStatement +from ietf.group.models import Group +from ietf.liaisons.models import LiaisonStatement # A slightly funny feed class, the 'object' is really # just a dict with some parameters that items() uses # to construct a queryset. class Liaisons(Feed): feed_type = Atom1Feed + def get_object(self, bits): obj = {} if bits[0] == 'recent': @@ -27,77 +25,50 @@ class Liaisons(Feed): obj['title'] = 'Recent Liaison Statements' obj['limit'] = 15 return obj - if bits[0] == 'from': + + if bits[0] == 'from': if len(bits) != 2: raise FeedDoesNotExist - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - try: - group = Group.objects.get(acronym=bits[1]) - obj['filter'] = { 'from_group': group } - obj['title'] = u'Liaison Statements from %s' % group.name + try: + group = Group.objects.get(acronym=bits[1]) + obj['filter'] = { 'from_group': group } + obj['title'] = u'Liaison Statements from %s' % group.name + return obj + except Group.DoesNotExist: + # turn all-nonword characters into one-character + # wildcards to make it easier to construct a URL that + # matches + search_string = re.sub(r"[^a-zA-Z1-9]", ".", bits[1]) + statements = LiaisonStatement.objects.filter(from_name__iregex=search_string) + if statements: + name = statements[0].from_name + obj['filter'] = { 'from_name': name } + obj['title'] = u'Liaison Statements from %s' % name return obj - except Group.DoesNotExist: - # turn all-nonword characters into one-character - # wildcards to make it easier to construct the URL - search_string = re.sub(r"[^a-zA-Z1-9]", ".", bits[1]) - statements = LiaisonStatement.objects.filter(from_name__iregex=search_string) - if statements: - name = statements[0].from_name - obj['filter'] = { 'from_name': name } - obj['title'] = u'Liaison Statements from %s' % name - return obj - else: - raise FeedDoesNotExist - try: - acronym = Acronym.objects.get(acronym=bits[1]) - obj['filter'] = {'from_id': acronym.acronym_id} - body = bits[1] - except Acronym.DoesNotExist: - # Find body matches. Turn all non-word characters - # into wildcards for the like search. - # Note that supplying sql here means that this is - # mysql-specific (e.g., postgresql wants 'ilike' for - # the same operation) - body_list = FromBodies.objects.values('from_id','body_name').extra(where=['body_name like "%s"' % re.sub('\W', '_', bits[1])]) - if not body_list: - raise FeedDoesNotExist - frmlist = [b['from_id'] for b in body_list] - # Assume that all of the matches have the same name. - # This is not guaranteed (e.g., a url like '-----------' - # will match several bodies) but is true of well-formed - # inputs. - body = body_list[0]['body_name'] - obj['filter'] = {'from_id__in': frmlist} - obj['title'] = 'Liaison Statements from %s' % body - return obj - if bits[0] == 'to': + else: + raise FeedDoesNotExist + + if bits[0] == 'to': if len(bits) != 2: raise FeedDoesNotExist - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - obj['filter'] = dict(to_name__icontains=bits[1]) - else: - # The schema uses two different fields for the same - # basic purpose, depending on whether it's a Secretariat-submitted - # or Liaison-tool-submitted document. - obj['q'] = [ (Q(by_secretariat=0) & Q(to_body__icontains=bits[1])) | (Q(by_secretariat=1) & Q(submitter_name__icontains=bits[1])) ] - obj['title'] = 'Liaison Statements where to matches %s' % bits[1] - return obj + obj['filter'] = dict(to_name__icontains=bits[1]) + obj['title'] = 'Liaison Statements where to matches %s' % bits[1] + return obj + if bits[0] == 'subject': if len(bits) != 2: raise FeedDoesNotExist - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - obj['q'] = [ Q(title__icontains=bits[1]) | Q(attachments__title__icontains=bits[1]) ] - else: - obj['q'] = [ Q(title__icontains=bits[1]) | Q(uploads__file_title__icontains=bits[1]) ] - obj['title'] = 'Liaison Statements where subject matches %s' % bits[1] + + obj['q'] = [ Q(title__icontains=bits[1]) | Q(attachments__title__icontains=bits[1]) ] + obj['title'] = 'Liaison Statements where subject matches %s' % bits[1] return obj raise FeedDoesNotExist def get_feed(self, url=None): - if not url: - raise FeedDoesNotExist - else: + if url: return Feed.get_feed(self, url=url) + else: + raise FeedDoesNotExist def title(self, obj): return obj['title'] @@ -106,12 +77,16 @@ class Liaisons(Feed): # no real equivalent for any objects return '/liaison/' + def item_link(self, obj): + # no real equivalent for any objects + return urlreverse("liaison_detail", kwargs={ "object_id": obj.pk }) + def description(self, obj): return self.title(obj) def items(self, obj): # Start with the common queryset - qs = LiaisonDetail.objects.all().order_by("-submitted_date") + qs = LiaisonStatement.objects.all().order_by("-submitted") if obj.has_key('q'): qs = qs.filter(*obj['q']) if obj.has_key('filter'): @@ -123,7 +98,7 @@ class Liaisons(Feed): def item_pubdate(self, item): # this method needs to return a datetime instance, even # though the database has only date, not time - return datetime.combine(item.submitted_date, time(0,0,0)) + return item.submitted def item_author_name(self, item): - return item.from_body() + return item.from_name diff --git a/ietf/liaisons/forms.py b/ietf/liaisons/forms.py index 0cfc391f7..8c895fc1f 100644 --- a/ietf/liaisons/forms.py +++ b/ietf/liaisons/forms.py @@ -1,4 +1,4 @@ -import datetime +import datetime, os from email.utils import parseaddr from django import forms @@ -8,26 +8,31 @@ from django.forms.util import ErrorList from django.core.validators import email_re from django.template.loader import render_to_string -from ietf.idtracker.models import PersonOrOrgInfo from ietf.liaisons.accounts import (can_add_outgoing_liaison, can_add_incoming_liaison, get_person_for_user, is_secretariat, is_sdo_liaison_manager) -from ietf.liaisons.models import LiaisonDetail, Uploads, OutgoingLiaisonApproval, SDOs from ietf.liaisons.utils import IETFHM from ietf.liaisons.widgets import (FromWidget, ReadOnlyWidget, ButtonWidget, ShowAttachmentsWidget, RelatedLiaisonWidget) +from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName +from ietf.group.models import Group, Role +from ietf.person.models import Person, Email +from ietf.doc.models import Document -class LiaisonForm(forms.ModelForm): - +class LiaisonForm(forms.Form): + person = forms.ModelChoiceField(Person.objects.all()) from_field = forms.ChoiceField(widget=FromWidget, label=u'From') replyto = forms.CharField(label=u'Reply to') organization = forms.ChoiceField() to_poc = forms.CharField(widget=ReadOnlyWidget, label="POC", required=False) + response_contact = forms.CharField(required=False, max_length=255) + technical_contact = forms.CharField(required=False, max_length=255) cc1 = forms.CharField(widget=forms.Textarea, label="CC", required=False, help_text='Please insert one email address per line') - purpose_text = forms.CharField(widget=forms.Textarea, label='Other purpose') + purpose = forms.ChoiceField() deadline_date = forms.DateField(label='Deadline') submitted_date = forms.DateField(label='Submission date', initial=datetime.date.today()) title = forms.CharField(label=u'Title') + body = forms.CharField(widget=forms.Textarea, required=False) attachments = forms.CharField(label='Attachments', widget=ShowAttachmentsWidget, required=False) attach_title = forms.CharField(label='Title', required=False) attach_file = forms.FileField(label='File', required=False) @@ -36,38 +41,51 @@ class LiaisonForm(forms.ModelForm): require=['id_attach_title', 'id_attach_file'], required_label='title and file'), required=False) - related_to = forms.ModelChoiceField(LiaisonDetail.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False) + related_to = forms.ModelChoiceField(LiaisonStatement.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False) fieldsets = [('From', ('from_field', 'replyto')), ('To', ('organization', 'to_poc')), ('Other email addresses', ('response_contact', 'technical_contact', 'cc1')), - ('Purpose', ('purpose', 'purpose_text', 'deadline_date')), + ('Purpose', ('purpose', 'deadline_date')), ('References', ('related_to', )), ('Liaison Statement', ('title', 'submitted_date', 'body', 'attachments')), ('Add attachment', ('attach_title', 'attach_file', 'attach_button')), ] - class Meta: - model = LiaisonDetail - - class Media: - js = ("/js/jquery-1.5.1.min.js", - "/js/jquery-ui-1.8.11.custom.min.js", - "/js/liaisons.js", ) - - css = {'all': ("/css/liaisons.css", - "/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css")} - def __init__(self, user, *args, **kwargs): self.user = user self.fake_person = None self.person = get_person_for_user(user) if kwargs.get('data', None): - kwargs['data'].update({'person': self.person.pk}) if is_secretariat(self.user) and 'from_fake_user' in kwargs['data'].keys(): - self.fake_person = PersonOrOrgInfo.objects.get(pk=kwargs['data']['from_fake_user']) + self.fake_person = Person.objects.get(pk=kwargs['data']['from_fake_user']) kwargs['data'].update({'person': self.fake_person.pk}) + else: + kwargs['data'].update({'person': self.person.pk}) + + self.instance = kwargs.pop("instance", None) + super(LiaisonForm, self).__init__(*args, **kwargs) + + # now copy in values from instance, like a ModelForm + if self.instance: + self.initial["person"] = self.instance.from_contact.person_id if self.instance.from_contact else None + self.initial["replyto"] = self.instance.reply_to + self.initial["to_poc"] = self.instance.to_contact + self.initial["response_contact"] = self.instance.response_contact + self.initial["technical_contact"] = self.instance.technical_contact + self.initial["cc1"] = self.instance.cc + self.initial["purpose"] = self.instance.purpose.order + self.initial["deadline_date"] = self.instance.deadline + self.initial["submitted_date"] = self.instance.submitted.date() if self.instance.submitted else None + self.initial["title"] = self.instance.title + self.initial["body"] = self.instance.body + self.initial["attachments"] = self.instance.attachments.all() + self.initial["related_to"] = self.instance.related_to_id + if "approved" in self.fields: + self.initial["approved"] = bool(self.instance.approved) + + self.fields["purpose"].choices = [("", "---------")] + [(str(l.order), l.name) for l in LiaisonStatementPurposeName.objects.all()] self.hm = IETFHM self.set_from_field() self.set_replyto_field() @@ -81,25 +99,19 @@ class LiaisonForm(forms.ModelForm): def set_required_fields(self): purpose = self.data.get('purpose', None) - if purpose == '5': - self.fields['purpose_text'].required=True - else: - self.fields['purpose_text'].required=False if purpose in ['1', '2']: - self.fields['deadline_date'].required=True + self.fields['deadline_date'].required = True else: - self.fields['deadline_date'].required=False + self.fields['deadline_date'].required = False def reset_required_fields(self): - self.fields['purpose_text'].required=True - self.fields['deadline_date'].required=True + self.fields['deadline_date'].required = True def set_from_field(self): assert NotImplemented def set_replyto_field(self): - email = self.person.email() - self.fields['replyto'].initial = email and email[1] + self.fields['replyto'].initial = self.person.email()[1] def set_organization_field(self): assert NotImplemented @@ -171,7 +183,7 @@ class LiaisonForm(forms.ModelForm): return self.hm.get_entity_by_key(organization_key) def get_poc(self, organization): - return ', '.join([i.email()[1] for i in organization.get_poc()]) + return ', '.join(u"%s <%s>" % i.email() for i in organization.get_poc()) def clean_cc1(self): value = self.cleaned_data.get('cc1', '') @@ -191,34 +203,53 @@ class LiaisonForm(forms.ModelForm): return ','.join(result) def get_cc(self, from_entity, to_entity): - #Old automatic Cc code, now we retrive it from cleaned_data - #persons = to_entity.get_cc(self.person) - #persons += from_entity.get_from_cc(self.person) - #return ', '.join(['%s <%s>' % i.email() for i in persons]) - cc = self.cleaned_data.get('cc1', '') - return cc + return self.cleaned_data.get('cc1', '') def save(self, *args, **kwargs): - liaison = super(LiaisonForm, self).save(*args, **kwargs) - self.save_extra_fields(liaison) - self.save_attachments(liaison) - return liaison + l = self.instance + if not l: + l = LiaisonStatement() + + l.title = self.cleaned_data["title"] + l.purpose = LiaisonStatementPurposeName.objects.get(order=self.cleaned_data["purpose"]) + l.body = self.cleaned_data["body"].strip() + l.deadline = self.cleaned_data["deadline_date"] + l.related_to = self.cleaned_data["related_to"] + l.reply_to = self.cleaned_data["replyto"] + l.response_contact = self.cleaned_data["response_contact"] + l.technical_contact = self.cleaned_data["technical_contact"] + + now = datetime.datetime.now() + + l.modified = now + l.submitted = datetime.datetime.combine(self.cleaned_data["submitted_date"], now.time()) + if not l.approved: + l.approved = now + + self.save_extra_fields(l) + + l.save() # we have to save here to make sure we get an id for the attachments + self.save_attachments(l) + + return l def save_extra_fields(self, liaison): - now = datetime.datetime.now() - liaison.last_modified_date = now from_entity = self.get_from_entity() - liaison.from_raw_body = from_entity.name - liaison.from_raw_code = self.cleaned_data.get('from_field') + liaison.from_name = from_entity.name + liaison.from_group = from_entity.obj + e = self.cleaned_data["person"].email_set.order_by('-active', '-time') + if e: + liaison.from_contact = e[0] + organization = self.get_to_entity() - liaison.to_raw_code = self.cleaned_data.get('organization') - liaison.to_body = organization.name - liaison.to_poc = self.get_poc(organization) - liaison.submitter_name, liaison.submitter_email = self.person.email() - liaison.cc1 = self.get_cc(from_entity, organization) - liaison.save() + liaison.to_name = organization.name + liaison.to_group = organization.obj + liaison.to_contact = self.get_poc(organization) + + liaison.cc = self.get_cc(from_entity, organization) def save_attachments(self, instance): + written = instance.attachments.all().count() for key in self.files.keys(): title_key = key.replace('file', 'title') if not key.startswith('attach_file_') or not title_key in self.data.keys(): @@ -229,13 +260,18 @@ class LiaisonForm(forms.ModelForm): extension = '.' + extension[1] else: extension = '' - attach = Uploads.objects.create( - file_title = self.data.get(title_key), - person = self.person, - detail = instance, - file_extension = extension, + written += 1 + name = instance.name() + ("-attachment-%s" % written) + attach, _ = Document.objects.get_or_create( + name = name, + defaults=dict( + title = self.data.get(title_key), + type_id = "liai-att", + external_url = name + extension, # strictly speaking not necessary, but just for the time being ... + ) ) - attach_file = open('%sfile%s%s' % (settings.LIAISON_ATTACH_PATH, attach.pk, attach.file_extension), 'w') + instance.attachments.add(attach) + attach_file = open(os.path.join(settings.LIAISON_ATTACH_PATH, attach.name + extension), 'w') attach_file.write(attached_file.read()) attach_file.close() @@ -245,8 +281,7 @@ class LiaisonForm(forms.ModelForm): exclude_filter = {'pk': self.instance.pk} else: exclude_filter = {} - exists = bool(LiaisonDetail.objects.exclude(**exclude_filter).filter(title__iexact=title).count()) - if exists: + if LiaisonStatement.objects.exclude(**exclude_filter).filter(title__iexact=title).exists(): raise forms.ValidationError('A liaison statement with the same title has previously been submitted.') return title @@ -255,20 +290,26 @@ class IncomingLiaisonForm(LiaisonForm): def set_from_field(self): if is_secretariat(self.user): - sdos = SDOs.objects.all() + sdos = Group.objects.filter(type="sdo", state="active") else: - sdo_managed = [i.sdo for i in self.person.liaisonmanagers_set.all()] - sdo_authorized = [i.sdo for i in self.person.sdoauthorizedindividual_set.all()] - sdos = set(sdo_managed).union(sdo_authorized) - self.fields['from_field'].choices = [('sdo_%s' % i.pk, i.sdo_name) for i in sdos] + sdos = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name__in=("liaiman", "auth")).distinct() + self.fields['from_field'].choices = [('sdo_%s' % i.pk, i.name) for i in sdos.order_by("name")] self.fields['from_field'].widget.submitter = unicode(self.person) + def set_replyto_field(self): + e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["liaiman", "auth"]) + if e: + addr = e[0].address + else: + addr = self.person.email_address() + self.fields['replyto'].initial = addr + def set_organization_field(self): self.fields['organization'].choices = self.hm.get_all_incoming_entities() def get_post_only(self): from_entity = self.get_from_entity() - if is_secretariat(self.user) or self.person.sdoauthorizedindividual_set.filter(sdo=from_entity.obj): + if is_secretariat(self.user) or Role.objects.filter(person=self.person, group=from_entity.obj, name="auth"): return False return True @@ -278,6 +319,9 @@ class IncomingLiaisonForm(LiaisonForm): return super(IncomingLiaisonForm, self).clean() +def liaison_manager_sdos(person): + return Group.objects.filter(type="sdo", state="active", role__person=person, role__name="liaiman").distinct() + class OutgoingLiaisonForm(LiaisonForm): to_poc = forms.CharField(label="POC", required=True) @@ -301,17 +345,24 @@ class OutgoingLiaisonForm(LiaisonForm): all_entities += i[1] if all_entities: self.fields['from_field'].widget.full_power_on = [i[0] for i in all_entities] - self.fields['from_field'].widget.reduced_to_set = ['sdo_%s' % i.sdo.pk for i in self.person.liaisonmanagers_set.all().distinct()] + self.fields['from_field'].widget.reduced_to_set = ['sdo_%s' % i.pk for i in liaison_manager_sdos(self.person)] else: self.fields['from_field'].choices = self.hm.get_entities_for_person(self.person) self.fields['from_field'].widget.submitter = unicode(self.person) self.fieldsets[0] = ('From', ('from_field', 'replyto', 'approved')) + def set_replyto_field(self): + e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["ad", "chair"]) + if e: + addr = e[0].address + else: + addr = self.person.email_address() + self.fields['replyto'].initial = addr + def set_organization_field(self): # If the user is a liaison manager and is nothing more, reduce the To field to his SDOs if not self.hm.get_entities_for_person(self.person) and is_sdo_liaison_manager(self.person): - sdos = [i.sdo for i in self.person.liaisonmanagers_set.all().distinct()] - self.fields['organization'].choices = [('sdo_%s' % i.pk, i.sdo_name) for i in sdos] + self.fields['organization'].choices = [('sdo_%s' % i.pk, i.name) for i in liaison_manager_sdos(self.person)] else: self.fields['organization'].choices = self.hm.get_all_outgoing_entities() self.fieldsets[1] = ('To', ('organization', 'other_organization', 'to_poc')) @@ -336,16 +387,9 @@ class OutgoingLiaisonForm(LiaisonForm): from_entity = self.get_from_entity() needs_approval = from_entity.needs_approval(self.person) if not needs_approval or self.cleaned_data.get('approved', False): - approved = True - approval_date = datetime.datetime.now() + liaison.approved = datetime.datetime.now() else: - approved = False - approval_date = None - approval = OutgoingLiaisonApproval.objects.create( - approved = approved, - approval_date = approval_date) - liaison.approval = approval - liaison.save() + liaison.approved = None def clean_to_poc(self): value = self.cleaned_data.get('to_poc', None) @@ -361,10 +405,10 @@ class OutgoingLiaisonForm(LiaisonForm): person = self.fake_person or self.person for i in self.hm.get_entities_for_person(person): all_entities += i[1] - # If the from entity is one in which the user has full privileges the to entity could be anyone + # If the from entity is one in wich the user has full privileges the to entity could be anyone if from_code in [i[0] for i in all_entities]: return to_code - sdo_codes = ['sdo_%s' % i.sdo.pk for i in person.liaisonmanagers_set.all().distinct()] + sdo_codes = ['sdo_%s' % i.pk for i in liaison_manager_sdos(person)] if to_code in sdo_codes: return to_code entity = self.get_to_entity() @@ -384,35 +428,28 @@ class EditLiaisonForm(LiaisonForm): cc1 = forms.CharField(widget=forms.TextInput, label="CC", required=False) class Meta: - model = LiaisonDetail fields = ('from_raw_body', 'to_body', 'to_poc', 'cc1', 'last_modified_date', 'title', - 'response_contact', 'technical_contact', 'purpose_text', 'body', + 'response_contact', 'technical_contact', 'body', 'deadline_date', 'purpose', 'replyto', 'related_to') def __init__(self, *args, **kwargs): super(EditLiaisonForm, self).__init__(*args, **kwargs) self.edit = True - self.initial.update({'attachments': self.instance.uploads_set.all()}) - self.fields['submitted_date'].initial = self.instance.submitted_date def set_from_field(self): - self.fields['from_field'].initial = self.instance.from_body + self.fields['from_field'].initial = self.instance.from_name def set_replyto_field(self): - self.fields['replyto'].initial = self.instance.replyto + self.fields['replyto'].initial = self.instance.reply_to def set_organization_field(self): - self.fields['organization'].initial = self.instance.to_body + self.fields['organization'].initial = self.instance.to_name def save_extra_fields(self, liaison): - now = datetime.datetime.now() - liaison.last_modified_date = now - liaison.from_raw_body = self.cleaned_data.get('from_field') - liaison.to_body = self.cleaned_data.get('organization') - liaison.to_poc = self.cleaned_data['to_poc'] - liaison.cc1 = self.cleaned_data['cc1'] - liaison.save() - + liaison.from_name = self.cleaned_data.get('from_field') + liaison.to_name = self.cleaned_data.get('organization') + liaison.to_contact = self.cleaned_data['to_poc'] + liaison.cc = self.cleaned_data['cc1'] def liaison_form_factory(request, **kwargs): user = request.user @@ -426,5 +463,3 @@ def liaison_form_factory(request, **kwargs): return IncomingLiaisonForm(user, **kwargs) return None -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.formsREDESIGN import * diff --git a/ietf/liaisons/formsREDESIGN.py b/ietf/liaisons/formsREDESIGN.py deleted file mode 100644 index efe2c6eb4..000000000 --- a/ietf/liaisons/formsREDESIGN.py +++ /dev/null @@ -1,474 +0,0 @@ -import datetime, os -from email.utils import parseaddr - -from django import forms -from django.conf import settings -from django.db.models import Q -from django.forms.util import ErrorList -from django.core.validators import email_re -from django.template.loader import render_to_string - -from ietf.liaisons.accounts import (can_add_outgoing_liaison, can_add_incoming_liaison, - get_person_for_user, is_secretariat, is_sdo_liaison_manager) -from ietf.liaisons.utils import IETFHM -from ietf.liaisons.widgets import (FromWidget, ReadOnlyWidget, ButtonWidget, - ShowAttachmentsWidget, RelatedLiaisonWidget) -from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName -from ietf.liaisons.proxy import LiaisonDetailProxy -from ietf.group.models import Group, Role -from ietf.person.models import Person, Email -from ietf.doc.models import Document - - -class LiaisonForm(forms.Form): - person = forms.ModelChoiceField(Person.objects.all()) - from_field = forms.ChoiceField(widget=FromWidget, label=u'From') - replyto = forms.CharField(label=u'Reply to') - organization = forms.ChoiceField() - to_poc = forms.CharField(widget=ReadOnlyWidget, label="POC", required=False) - response_contact = forms.CharField(required=False, max_length=255) - technical_contact = forms.CharField(required=False, max_length=255) - cc1 = forms.CharField(widget=forms.Textarea, label="CC", required=False, help_text='Please insert one email address per line') - purpose = forms.ChoiceField() - purpose_text = forms.CharField(widget=forms.Textarea, label='Other purpose') - deadline_date = forms.DateField(label='Deadline') - submitted_date = forms.DateField(label='Submission date', initial=datetime.date.today()) - title = forms.CharField(label=u'Title') - body = forms.CharField(widget=forms.Textarea, required=False) - attachments = forms.CharField(label='Attachments', widget=ShowAttachmentsWidget, required=False) - attach_title = forms.CharField(label='Title', required=False) - attach_file = forms.FileField(label='File', required=False) - attach_button = forms.CharField(label='', - widget=ButtonWidget(label='Attach', show_on='id_attachments', - require=['id_attach_title', 'id_attach_file'], - required_label='title and file'), - required=False) - related_to = forms.ModelChoiceField(LiaisonStatement.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False) - - fieldsets = [('From', ('from_field', 'replyto')), - ('To', ('organization', 'to_poc')), - ('Other email addresses', ('response_contact', 'technical_contact', 'cc1')), - ('Purpose', ('purpose', 'purpose_text', 'deadline_date')), - ('References', ('related_to', )), - ('Liaison Statement', ('title', 'submitted_date', 'body', 'attachments')), - ('Add attachment', ('attach_title', 'attach_file', 'attach_button')), - ] - - class Media: - js = ("/js/jquery-1.5.1.min.js", - "/js/jquery-ui-1.8.11.custom.min.js", - "/js/liaisons.js", ) - - css = {'all': ("/css/liaisons.css", - "/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css")} - - def __init__(self, user, *args, **kwargs): - self.user = user - self.fake_person = None - self.person = get_person_for_user(user) - if kwargs.get('data', None): - if is_secretariat(self.user) and 'from_fake_user' in kwargs['data'].keys(): - self.fake_person = Person.objects.get(pk=kwargs['data']['from_fake_user']) - kwargs['data'].update({'person': self.fake_person.pk}) - else: - kwargs['data'].update({'person': self.person.pk}) - - self.instance = kwargs.pop("instance", None) - - super(LiaisonForm, self).__init__(*args, **kwargs) - - # now copy in values from instance, like a ModelForm - if self.instance: - for name, field in self.fields.iteritems(): - try: - x = getattr(self.instance, name) - if name == "purpose": # proxy has a name-clash on purpose so help it - x = x.order - - try: - x = x.pk # foreign keys need the .pk, not the actual object - except AttributeError: - pass - self.initial[name] = x - except AttributeError: - # we have some fields on the form that aren't in the model - pass - self.fields["purpose"].choices = [("", "---------")] + [(str(l.order), l.name) for l in LiaisonStatementPurposeName.objects.all()] - self.hm = IETFHM - self.set_from_field() - self.set_replyto_field() - self.set_organization_field() - - def __unicode__(self): - return self.as_div() - - def get_post_only(self): - return False - - def set_required_fields(self): - purpose = self.data.get('purpose', None) - if purpose == '5': - self.fields['purpose_text'].required=True - else: - self.fields['purpose_text'].required=False - if purpose in ['1', '2']: - self.fields['deadline_date'].required=True - else: - self.fields['deadline_date'].required=False - - def reset_required_fields(self): - self.fields['purpose_text'].required=True - self.fields['deadline_date'].required=True - - def set_from_field(self): - assert NotImplemented - - def set_replyto_field(self): - self.fields['replyto'].initial = self.person.email()[1] - - def set_organization_field(self): - assert NotImplemented - - def as_div(self): - return render_to_string('liaisons/liaisonform.html', {'form': self}) - - def get_fieldsets(self): - if not self.fieldsets: - yield dict(name=None, fields=self) - else: - for fieldset, fields in self.fieldsets: - fieldset_dict = dict(name=fieldset, fields=[]) - for field_name in fields: - if field_name in self.fields.keyOrder: - fieldset_dict['fields'].append(self[field_name]) - if not fieldset_dict['fields']: - # if there is no fields in this fieldset, we continue to next fieldset - continue - yield fieldset_dict - - def full_clean(self): - self.set_required_fields() - super(LiaisonForm, self).full_clean() - self.reset_required_fields() - - def has_attachments(self): - for key in self.files.keys(): - if key.startswith('attach_file_') and key.replace('file', 'title') in self.data.keys(): - return True - return False - - def check_email(self, value): - if not value: - return - emails = value.split(',') - for email in emails: - name, addr = parseaddr(email) - if not email_re.search(addr): - raise forms.ValidationError('Invalid email address: %s' % addr) - - def clean_response_contact(self): - value = self.cleaned_data.get('response_contact', None) - self.check_email(value) - return value - - def clean_technical_contact(self): - value = self.cleaned_data.get('technical_contact', None) - self.check_email(value) - return value - - def clean_reply_to(self): - value = self.cleaned_data.get('reply_to', None) - self.check_email(value) - return value - - def clean(self): - if not self.cleaned_data.get('body', None) and not self.has_attachments(): - self._errors['body'] = ErrorList([u'You must provide a body or attachment files']) - self._errors['attachments'] = ErrorList([u'You must provide a body or attachment files']) - return self.cleaned_data - - def get_from_entity(self): - organization_key = self.cleaned_data.get('from_field') - return self.hm.get_entity_by_key(organization_key) - - def get_to_entity(self): - organization_key = self.cleaned_data.get('organization') - return self.hm.get_entity_by_key(organization_key) - - def get_poc(self, organization): - return ', '.join(u"%s <%s>" % i.email() for i in organization.get_poc()) - - def clean_cc1(self): - value = self.cleaned_data.get('cc1', '') - result = [] - errors = [] - for address in value.split('\n'): - address = address.strip(); - if not address: - continue - try: - self.check_email(address) - except forms.ValidationError: - errors.append(address) - result.append(address) - if errors: - raise forms.ValidationError('Invalid email addresses: %s' % ', '.join(errors)) - return ','.join(result) - - def get_cc(self, from_entity, to_entity): - #Old automatic Cc code, now we retrive it from cleaned_data - #persons = to_entity.get_cc(self.person) - #persons += from_entity.get_from_cc(self.person) - #return ', '.join(['%s <%s>' % i.email() for i in persons]) - cc = self.cleaned_data.get('cc1', '') - return cc - - def save(self, *args, **kwargs): - l = self.instance - if not l: - l = LiaisonDetailProxy() - - l.title = self.cleaned_data["title"] - l.purpose = LiaisonStatementPurposeName.objects.get(order=self.cleaned_data["purpose"]) - l.body = self.cleaned_data["body"].strip() - l.deadline = self.cleaned_data["deadline_date"] - l.related_to = self.cleaned_data["related_to"] - l.reply_to = self.cleaned_data["replyto"] - l.response_contact = self.cleaned_data["response_contact"] - l.technical_contact = self.cleaned_data["technical_contact"] - - now = datetime.datetime.now() - - l.modified = now - l.submitted = datetime.datetime.combine(self.cleaned_data["submitted_date"], now.time()) - if not l.approved: - l.approved = now - - self.save_extra_fields(l) - - l.save() # we have to save here to make sure we get an id for the attachments - self.save_attachments(l) - - return l - - def save_extra_fields(self, liaison): - from_entity = self.get_from_entity() - liaison.from_name = from_entity.name - liaison.from_group = from_entity.obj - e = self.cleaned_data["person"].email_set.order_by('-active') - if e: - liaison.from_contact = e[0] - - organization = self.get_to_entity() - liaison.to_name = organization.name - liaison.to_group = organization.obj - liaison.to_contact = self.get_poc(organization) - - liaison.cc = self.get_cc(from_entity, organization) - - def save_attachments(self, instance): - written = instance.attachments.all().count() - for key in self.files.keys(): - title_key = key.replace('file', 'title') - if not key.startswith('attach_file_') or not title_key in self.data.keys(): - continue - attached_file = self.files.get(key) - extension=attached_file.name.rsplit('.', 1) - if len(extension) > 1: - extension = '.' + extension[1] - else: - extension = '' - written += 1 - name = instance.name() + ("-attachment-%s" % written) - attach, _ = Document.objects.get_or_create( - name = name, - defaults=dict( - title = self.data.get(title_key), - type_id = "liai-att", - external_url = name + extension, # strictly speaking not necessary, but just for the time being ... - ) - ) - instance.attachments.add(attach) - attach_file = open(os.path.join(settings.LIAISON_ATTACH_PATH, attach.name + extension), 'w') - attach_file.write(attached_file.read()) - attach_file.close() - - def clean_title(self): - title = self.cleaned_data.get('title', None) - if self.instance and self.instance.pk: - exclude_filter = {'pk': self.instance.pk} - else: - exclude_filter = {} - exists = bool(LiaisonStatement.objects.exclude(**exclude_filter).filter(title__iexact=title).count()) - if exists: - raise forms.ValidationError('A liaison statement with the same title has previously been submitted.') - return title - - -class IncomingLiaisonForm(LiaisonForm): - - def set_from_field(self): - if is_secretariat(self.user): - sdos = Group.objects.filter(type="sdo", state="active") - else: - sdos = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name__in=("liaiman", "auth")).distinct() - self.fields['from_field'].choices = [('sdo_%s' % i.pk, i.name) for i in sdos.order_by("name")] - self.fields['from_field'].widget.submitter = unicode(self.person) - - def set_replyto_field(self): - e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["liaiman", "auth"]) - if e: - addr = e[0].address - else: - addr = self.person.email_address() - self.fields['replyto'].initial = addr - - def set_organization_field(self): - self.fields['organization'].choices = self.hm.get_all_incoming_entities() - - def get_post_only(self): - from_entity = self.get_from_entity() - if is_secretariat(self.user) or Role.objects.filter(person=self.person, group=from_entity.obj, name="auth"): - return False - return True - - def clean(self): - if 'send' in self.data.keys() and self.get_post_only(): - self._errors['from_field'] = ErrorList([u'As an IETF Liaison Manager you can not send an incoming liaison statements, you only can post them']) - return super(IncomingLiaisonForm, self).clean() - - -def liaison_manager_sdos(person): - return Group.objects.filter(type="sdo", state="active", role__person=person, role__name="liaiman").distinct() - -class OutgoingLiaisonForm(LiaisonForm): - - to_poc = forms.CharField(label="POC", required=True) - approved = forms.BooleanField(label="Obtained prior approval", required=False) - other_organization = forms.CharField(label="Other SDO", required=True) - - def get_to_entity(self): - organization_key = self.cleaned_data.get('organization') - organization = self.hm.get_entity_by_key(organization_key) - if organization_key == 'othersdo' and self.cleaned_data.get('other_organization', None): - organization.name=self.cleaned_data['other_organization'] - return organization - - def set_from_field(self): - if is_secretariat(self.user): - self.fields['from_field'].choices = self.hm.get_all_incoming_entities() - elif is_sdo_liaison_manager(self.person): - self.fields['from_field'].choices = self.hm.get_all_incoming_entities() - all_entities = [] - for i in self.hm.get_entities_for_person(self.person): - all_entities += i[1] - if all_entities: - self.fields['from_field'].widget.full_power_on = [i[0] for i in all_entities] - self.fields['from_field'].widget.reduced_to_set = ['sdo_%s' % i.pk for i in liaison_manager_sdos(self.person)] - else: - self.fields['from_field'].choices = self.hm.get_entities_for_person(self.person) - self.fields['from_field'].widget.submitter = unicode(self.person) - self.fieldsets[0] = ('From', ('from_field', 'replyto', 'approved')) - - def set_replyto_field(self): - e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["ad", "chair"]) - if e: - addr = e[0].address - else: - addr = self.person.email_address() - self.fields['replyto'].initial = addr - - def set_organization_field(self): - # If the user is a liaison manager and is nothing more, reduce the To field to his SDOs - if not self.hm.get_entities_for_person(self.person) and is_sdo_liaison_manager(self.person): - self.fields['organization'].choices = [('sdo_%s' % i.pk, i.name) for i in liaison_manager_sdos(self.person)] - else: - self.fields['organization'].choices = self.hm.get_all_outgoing_entities() - self.fieldsets[1] = ('To', ('organization', 'other_organization', 'to_poc')) - - def set_required_fields(self): - super(OutgoingLiaisonForm, self).set_required_fields() - organization = self.data.get('organization', None) - if organization == 'othersdo': - self.fields['other_organization'].required=True - else: - self.fields['other_organization'].required=False - - def reset_required_fields(self): - super(OutgoingLiaisonForm, self).reset_required_fields() - self.fields['other_organization'].required=True - - def get_poc(self, organization): - return self.cleaned_data['to_poc'] - - def save_extra_fields(self, liaison): - super(OutgoingLiaisonForm, self).save_extra_fields(liaison) - from_entity = self.get_from_entity() - needs_approval = from_entity.needs_approval(self.person) - if not needs_approval or self.cleaned_data.get('approved', False): - liaison.approved = datetime.datetime.now() - else: - liaison.approved = None - - def clean_to_poc(self): - value = self.cleaned_data.get('to_poc', None) - self.check_email(value) - return value - - def clean_organization(self): - to_code = self.cleaned_data.get('organization', None) - from_code = self.cleaned_data.get('from_field', None) - if not to_code or not from_code: - return to_code - all_entities = [] - person = self.fake_person or self.person - for i in self.hm.get_entities_for_person(person): - all_entities += i[1] - # If the from entity is one in wich the user has full privileges the to entity could be anyone - if from_code in [i[0] for i in all_entities]: - return to_code - sdo_codes = ['sdo_%s' % i.pk for i in liaison_manager_sdos(person)] - if to_code in sdo_codes: - return to_code - entity = self.get_to_entity() - entity_name = entity and entity.name or to_code - if self.fake_person: - raise forms.ValidationError('%s is not allowed to send a liaison to: %s' % (self.fake_person, entity_name)) - else: - raise forms.ValidationError('You are not allowed to send a liaison to: %s' % entity_name) - - -class EditLiaisonForm(LiaisonForm): - - from_field = forms.CharField(widget=forms.TextInput, label=u'From') - replyto = forms.CharField(label=u'Reply to', widget=forms.TextInput) - organization = forms.CharField(widget=forms.TextInput) - to_poc = forms.CharField(widget=forms.TextInput, label="POC", required=False) - cc1 = forms.CharField(widget=forms.TextInput, label="CC", required=False) - - class Meta: - fields = ('from_raw_body', 'to_body', 'to_poc', 'cc1', 'last_modified_date', 'title', - 'response_contact', 'technical_contact', 'purpose_text', 'body', - 'deadline_date', 'purpose', 'replyto', 'related_to') - - def __init__(self, *args, **kwargs): - super(EditLiaisonForm, self).__init__(*args, **kwargs) - self.edit = True - self.initial.update({'attachments': self.instance.uploads_set.all()}) - self.fields['submitted_date'].initial = self.instance.submitted_date - - def set_from_field(self): - self.fields['from_field'].initial = self.instance.from_body - - def set_replyto_field(self): - self.fields['replyto'].initial = self.instance.replyto - - def set_organization_field(self): - self.fields['organization'].initial = self.instance.to_body - - def save_extra_fields(self, liaison): - liaison.from_name = self.cleaned_data.get('from_field') - liaison.to_name = self.cleaned_data.get('organization') - liaison.to_contact = self.cleaned_data['to_poc'] - liaison.cc = self.cleaned_data['cc1'] - diff --git a/ietf/liaisons/mails.py b/ietf/liaisons/mails.py index db80b9d3c..3af748db5 100644 --- a/ietf/liaisons/mails.py +++ b/ietf/liaisons/mails.py @@ -8,14 +8,11 @@ from ietf.utils.mail import send_mail_text from ietf.liaisons.utils import role_persons_with_fixed_email from ietf.group.models import Role -def send_liaison_by_email(request, liaison, fake=False): - if liaison.is_pending(): # this conditional should definitely be at the caller, not here - return notify_pending_by_email(request, liaison, fake) - +def send_liaison_by_email(request, liaison): subject = u'New Liaison Statement, "%s"' % (liaison.title) from_email = settings.LIAISON_UNIVERSAL_FROM - to_email = liaison.to_poc.split(',') - cc = liaison.cc1.split(',') + to_email = liaison.to_contact.split(',') + cc = liaison.cc.split(',') if liaison.technical_contact: cc += liaison.technical_contact.split(',') if liaison.response_contact: @@ -29,7 +26,7 @@ def send_liaison_by_email(request, liaison, fake=False): send_mail_text(request, to_email, from_email, subject, body, cc=", ".join(cc), bcc=", ".join(bcc)) -def notify_pending_by_email(request, liaison, fake): +def notify_pending_by_email(request, liaison): # Broken: this does not find the list of approvers for the sending body # For now, we are sending to statements@ietf.org so the Secretariat can nudge diff --git a/ietf/liaisons/management/commands/check_liaison_deadlines.py b/ietf/liaisons/management/commands/check_liaison_deadlines.py index 7deeb4ecf..73dd585ef 100644 --- a/ietf/liaisons/management/commands/check_liaison_deadlines.py +++ b/ietf/liaisons/management/commands/check_liaison_deadlines.py @@ -5,78 +5,19 @@ from django.core.management.base import BaseCommand from django.template.loader import render_to_string from django.core.urlresolvers import reverse as urlreverse -from ietf.liaisons.models import LiaisonDetail -#from ietf.liaisons.mail import IETFEmailMessage -from ietf.utils.mail import send_mail_text - - -PREVIOUS_DAYS = { - 14: 'in two weeks', - 7: 'in one week', - 4: 'in four days', - 3: 'in three days', - 2: 'in two days', - 1: 'tomorrow', - 0: 'today'} +from ietf.liaisons.models import LiaisonStatement +from ietf.liaisons.mails import possibly_send_deadline_reminder class Command(BaseCommand): help = (u"Check liaison deadlines and send a reminder if we are close to a deadline") - def send_reminder(self, liaison, days_to_go): - if days_to_go < 0: - subject = '[Liaison OUT OF DATE] %s' % liaison.title - days_msg = 'is out of date for %s days' % (-days_to_go) - else: - subject = '[Liaison deadline %s] %s' % (PREVIOUS_DAYS[days_to_go], liaison.title) - days_msg = 'expires %s' % PREVIOUS_DAYS[days_to_go] - - from_email = settings.LIAISON_UNIVERSAL_FROM - to_email = liaison.to_poc.split(',') - cc = liaison.cc1.split(',') - if liaison.technical_contact: - cc += liaison.technical_contact.split(',') - if liaison.response_contact: - cc += liaison.response_contact.split(',') - bcc = ['statements@ietf.org'] - body = render_to_string('liaisons/liaison_deadline_mail.txt', - {'liaison': liaison, - 'days_msg': days_msg, - 'url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_approval_detail", kwargs=dict(object_id=liaison.pk)), - 'referenced_url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=liaison.related_to.pk)) if liaison.related_to else None, - }) - send_mail_text(context=None,to=to_email,frm=from_email,cc=cc,subject=subject,bcc=bcc,txt=body) - print 'Liaison %05s#: Deadline reminder Sent!' % liaison.pk - - #mail = IETFEmailMessage(subject=subject, - # to=to_email, - # from_email=from_email, - # cc=cc, - # bcc=bcc, - # body=body) - #if not settings.DEBUG: - # mail.send() - # print 'Liaison %05s#: Deadline reminder Sent!' % liaison.pk - #else: - # print 'Liaison %05s#: Deadline reminder Not Sent because in DEBUG mode!' % liaison.pk - def handle(self, *args, **options): today = datetime.date.today() - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.mails import possibly_send_deadline_reminder - from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail - - cutoff = today - datetime.timedelta(14) - - for l in LiaisonDetail.objects.filter(action_taken=False, deadline__gte=cutoff).exclude(deadline=None): - r = possibly_send_deadline_reminder(l) - if r: - print 'Liaison %05s#: Deadline reminder sent!' % liaison.pk - return - - query = LiaisonDetail.objects.filter(deadline_date__isnull=False, action_taken=False, deadline_date__gte=today - datetime.timedelta(14)) - for liaison in query: - delta = liaison.deadline_date - today - if delta.days < 0 or delta.days in PREVIOUS_DAYS.keys(): - self.send_reminder(liaison, delta.days) + cutoff = today - datetime.timedelta(14) + + for l in LiaisonStatement.objects.filter(action_taken=False, deadline__gte=cutoff).exclude(deadline=None): + r = possibly_send_deadline_reminder(l) + if r: + print 'Liaison %05s#: Deadline reminder sent!' % liaison.pk diff --git a/ietf/liaisons/management/commands/remind_update_sdo_list.py b/ietf/liaisons/management/commands/remind_update_sdo_list.py index 8c69093b8..fb37be74f 100644 --- a/ietf/liaisons/management/commands/remind_update_sdo_list.py +++ b/ietf/liaisons/management/commands/remind_update_sdo_list.py @@ -5,7 +5,8 @@ from django.core.mail import EmailMessage from django.core.management.base import BaseCommand from django.template.loader import render_to_string -from ietf.liaisons.models import SDOs +from ietf.group.models import Group +from ietf.liaisons.mails import send_sdo_reminder class Command(BaseCommand): @@ -16,56 +17,15 @@ class Command(BaseCommand): ) - def send_mail_to(self, person, sdo): - subject = 'Request for update list of authorized individuals' - email = person.email()[1] - name = ' '.join([i for i in (person.name_prefix, person.first_name, person.middle_initial, person.last_name, person.name_suffix) if i]) - authorized_list = [i.person for i in sdo.sdoauthorizedindividual_set.all()] - body = render_to_string('liaisons/sdo_reminder.txt', - {'manager_name': name, - 'sdo_name': sdo.sdo_name, - 'individuals': authorized_list, - }) - mail = EmailMessage(subject=subject, - to=[email], - from_email=settings.LIAISON_UNIVERSAL_FROM, - body = body) - if not settings.DEBUG: - mail.send() - msg = '%05s#: %s Mail Sent!' % (sdo.pk, sdo.sdo_name) - else: - msg = '%05s#: %s Mail Not Sent because in DEBUG mode!' % (sdo.pk, sdo.sdo_name) - return msg - def handle(self, *args, **options): sdo_pk = options.get('sdo_pk', None) return_output = options.get('return_output', False) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - msg_list = send_reminders_to_sdos(sdo_pk=sdo_pk) - return msg_list if return_output else None - - query = SDOs.objects.all().order_by('pk') - if sdo_pk: - query = query.filter(pk=sdo_pk) - - msg_list = [] - for sdo in query: - manager = sdo.liaisonmanager() - if manager: - msg = self.send_mail_to(manager.person, sdo) - else: - msg = '%05s#: %s has no liaison manager' % (sdo.pk, sdo.sdo_name) - print msg - msg_list.append(msg) - if return_output: - return msg_list + msg_list = send_reminders_to_sdos(sdo_pk=sdo_pk) + return msg_list if return_output else None def send_reminders_to_sdos(sdo_pk=None): - from ietf.group.models import Group - from ietf.liaisons.mails import send_sdo_reminder - sdos = Group.objects.filter(type="sdo").order_by('pk') if sdo_pk: sdos = sdos.filter(pk=sdo_pk) diff --git a/ietf/liaisons/models.py b/ietf/liaisons/models.py index d1b2fe832..6f596256c 100644 --- a/ietf/liaisons/models.py +++ b/ietf/liaisons/models.py @@ -1,369 +1,52 @@ # Copyright The IETF Trust 2007, All Rights Reserved -from django.conf import settings -from django.core.exceptions import ObjectDoesNotExist from django.db import models -from django.template.loader import render_to_string -from django.contrib.auth.models import User -from django.core.urlresolvers import reverse as urlreverse -from ietf.idtracker.models import Acronym, PersonOrOrgInfo, Area, IESGLogin -#from ietf.liaisons.mail import IETFEmailMessage -from ietf.utils.mail import send_mail_text -from ietf.ietfauth.models import LegacyLiaisonUser -from ietf.utils.admin import admin_link -class LiaisonPurpose(models.Model): - purpose_id = models.AutoField(primary_key=True) - purpose_text = models.CharField(blank=True, max_length=50) - def __str__(self): - return self.purpose_text - class Meta: - db_table = 'liaison_purpose' - -class FromBodies(models.Model): - from_id = models.AutoField(primary_key=True) - body_name = models.CharField(blank=True, max_length=35) - poc = models.ForeignKey(PersonOrOrgInfo, db_column='poc', null=True) - is_liaison_manager = models.BooleanField() - other_sdo = models.BooleanField() - email_priority = models.IntegerField(null=True, blank=True) - def __str__(self): - return self.body_name - class Meta: - db_table = 'from_bodies' - verbose_name = "From body" - verbose_name_plural = "From bodies" - contact_link = admin_link('poc', label='Contact') +from ietf.name.models import LiaisonStatementPurposeName +from ietf.doc.models import Document +from ietf.person.models import Email +from ietf.group.models import Group +class LiaisonStatement(models.Model): + title = models.CharField(blank=True, max_length=255) + purpose = models.ForeignKey(LiaisonStatementPurposeName) + body = models.TextField(blank=True) + deadline = models.DateField(null=True, blank=True) + related_to = models.ForeignKey('LiaisonStatement', blank=True, null=True) -class OutgoingLiaisonApproval(models.Model): - approved = models.BooleanField(default=True) - approval_date = models.DateField(null=True, blank=True) + from_group = models.ForeignKey(Group, related_name="liaisonstatement_from_set", null=True, blank=True, help_text="Sender group, if it exists") + from_name = models.CharField(max_length=255, help_text="Name of the sender body") + from_contact = models.ForeignKey(Email, blank=True, null=True) + to_group = models.ForeignKey(Group, related_name="liaisonstatement_to_set", null=True, blank=True, help_text="Recipient group, if it exists") + to_name = models.CharField(max_length=255, help_text="Name of the recipient body") + to_contact = models.CharField(blank=True, max_length=255, help_text="Contacts at recipient body") + reply_to = models.CharField(blank=True, max_length=255) -class LiaisonDetail(models.Model): - detail_id = models.AutoField(primary_key=True) - person = models.ForeignKey(PersonOrOrgInfo, null=True, db_column='person_or_org_tag') - submitted_date = models.DateField(null=True, blank=True) - last_modified_date = models.DateField(null=True, blank=True) - from_id = models.IntegerField(null=True, blank=True) - to_body = models.CharField(blank=True, null=True, max_length=255) - title = models.CharField(blank=True, null=True, max_length=255) - response_contact = models.CharField(blank=True, null=True, max_length=255) - technical_contact = models.CharField(blank=True, null=True, max_length=255) - purpose_text = models.TextField(blank=True, null=True, db_column='purpose') - body = models.TextField(blank=True,null=True) - deadline_date = models.DateField(null=True, blank=True) - cc1 = models.TextField(blank=True, null=True) - # unclear why cc2 is a CharField, but it's always - # either NULL or blank. - cc2 = models.CharField(blank=True, null=True, max_length=50) - submitter_name = models.CharField(blank=True, null=True, max_length=255) - submitter_email = models.CharField(blank=True, null=True, max_length=255) - by_secretariat = models.IntegerField(null=True, blank=True) - to_poc = models.CharField(blank=True, null=True, max_length=255) - to_email = models.CharField(blank=True, null=True, max_length=255) - purpose = models.ForeignKey(LiaisonPurpose,null=True) - replyto = models.CharField(blank=True, null=True, max_length=255) - from_raw_body = models.CharField(blank=True, null=True, max_length=255) - from_raw_code = models.CharField(blank=True, null=True, max_length=255) - to_raw_code = models.CharField(blank=True, null=True, max_length=255) - approval = models.ForeignKey(OutgoingLiaisonApproval, blank=True, null=True) - action_taken = models.BooleanField(default=False, db_column='taken_care') - related_to = models.ForeignKey('LiaisonDetail', blank=True, null=True) - def __str__(self): - return self.title or "" - def __unicode__(self): - return self.title or "" - def from_body(self): - """The from_raw_body stores the name of the entity - sending the liaison. - For legacy liaisons (the ones with empty from_raw_body) - the legacy_from_body() is returned.""" - if not self.from_raw_body: - return self.legacy_from_body() - return self.from_raw_body - def from_sdo(self): - try: - name = FromBodies.objects.get(pk=self.from_id).body_name - sdo = SDOs.objects.get(sdo_name=name) - return sdo - except ObjectDoesNotExist: - return None - def legacy_from_body(self): - """The from_id field is a foreign key for either - FromBodies or Acronyms, depending on whether it's - the IETF or not. There is no flag field saying - which, so we just try it. If the index values - overlap, then this function will be ambiguous - and will return the value from FromBodies. Current - acronym IDs start at 925 so the day of reckoning - is not nigh.""" - try: - from_body = FromBodies.objects.get(pk=self.from_id) - return from_body.body_name - except ObjectDoesNotExist: - pass - try: - acronym = Acronym.objects.get(pk=self.from_id) - try: - x = acronym.area - kind = "AREA" - except Area.DoesNotExist: - kind = "WG" - return "IETF %s %s" % (acronym.acronym.upper(), kind) - except ObjectDoesNotExist: - pass - return "" % self.from_id - def from_email(self): - """If there is an entry in from_bodies, it has - the desired email priority. However, if it's from - an IETF WG, there is no entry in from_bodies, so - default to 1.""" - try: - from_body = FromBodies.objects.get(pk=self.from_id) - email_priority = from_body.email_priority - except FromBodies.DoesNotExist: - email_priority = 1 - return self.person.emailaddress_set.all().get(priority=email_priority) - def get_absolute_url(self): - return '/liaison/%d/' % self.detail_id - class Meta: - db_table = 'liaison_detail' + response_contact = models.CharField(blank=True, max_length=255) + technical_contact = models.CharField(blank=True, max_length=255) + cc = models.TextField(blank=True) - def notify_pending_by_email(self, fake): - from ietf.liaisons.utils import IETFHM + submitted = models.DateTimeField(null=True, blank=True) + modified = models.DateTimeField(null=True, blank=True) + approved = models.DateTimeField(null=True, blank=True) - from_entity = IETFHM.get_entity_by_key(self.from_raw_code) - if not from_entity: - return None - to_email = [] - for person in from_entity.can_approve(): - to_email.append('%s <%s>' % person.email()) - subject = 'New Liaison Statement, "%s" needs your approval' % (self.title) - from_email = settings.LIAISON_UNIVERSAL_FROM - body = render_to_string('liaisons/pending_liaison_mail.txt', { - 'liaison': self, - 'url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_approval_detail", kwargs=dict(object_id=self.pk)), - 'referenced_url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=self.related_to.pk)) if self.related_to else None, - }) - send_mail_text(context=None, to=to_email, frm=from_email, subject=subject, txt = body) - #mail = IETFEmailMessage(subject=subject, - # to=to_email, - # from_email=from_email, - # body = body) - #if not fake: - # mail.send() - #return mail + action_taken = models.BooleanField(default=False) - def send_by_email(self, fake=False): - if self.is_pending(): - return self.notify_pending_by_email(fake) - subject = 'New Liaison Statement, "%s"' % (self.title) - from_email = settings.LIAISON_UNIVERSAL_FROM - to_email = self.to_poc.split(',') - cc = self.cc1.split(',') - if self.technical_contact: - cc += self.technical_contact.split(',') - if self.response_contact: - cc += self.response_contact.split(',') - bcc = ['statements@ietf.org'] - body = render_to_string('liaisons/liaison_mail.txt', { - 'liaison': self, - 'url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=self.pk)), - 'referenced_url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=self.related_to.pk)) if self.related_to else None, - }) - send_mail_text(context=None,to=to_email,frm=from_email,subject=subject,txt=body,cc=cc,bcc=bcc) - #mail = IETFEmailMessage(subject=subject, - # to=to_email, - # from_email=from_email, - # cc = cc, - # bcc = bcc, - # body = body) - #if not fake: - # mail.send() - #return mail + attachments = models.ManyToManyField(Document, blank=True) - def is_pending(self): - return bool(self.approval and not self.approval.approved) - - -class SDOs(models.Model): - sdo_id = models.AutoField(primary_key=True, verbose_name='ID') - sdo_name = models.CharField(blank=True, max_length=255, verbose_name='SDO Name') - def __str__(self): - return self.sdo_name - def liaisonmanager(self): - try: - return self.liaisonmanagers_set.all()[0] - except: - return None - def sdo_contact(self): - try: - return self.sdoauthorizedindividual_set.all()[0] - except: - return None - class Meta: - verbose_name = 'SDO' - verbose_name_plural = 'SDOs' - db_table = 'sdos' - ordering = ('sdo_name', ) - liaisonmanager_link = admin_link('liaisonmanager', label='Liaison') - sdo_contact_link = admin_link('sdo_contact') - -class LiaisonStatementManager(models.Model): - person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag') - sdo = models.ForeignKey(SDOs, verbose_name='SDO') - def __unicode__(self): - return '%s (%s)' % (self.person, self.sdo) - class Meta: - abstract = True - # Helper functions, for use in the admin interface - def login_name(self): - login_name = None - try: - login_name = IESGLogin.objects.get(person=self.person).login_name - if User.objects.filter(username=login_name).count(): - return login_name - except IESGLogin.DoesNotExist: - pass - try: - login_name = LegacyLiaisonUser.objects.get(person=self.person).login_name - except LegacyLiaisonUser.DoesNotExist: - pass - return login_name - def user(self): - login_name = self.login_name() - user = None - if login_name: - try: - return User.objects.get(username=login_name), login_name - except User.DoesNotExist: - pass - return None, login_name - def user_name(self): - user, login_name = self.user() - if user: - return u'%s' % (user.id, login_name) + def name(self): + from django.template.defaultfilters import slugify + if self.from_group: + frm = self.from_group.acronym or self.from_group.name else: - if login_name: - return u'Add login: %s' % (login_name, login_name) - else: - return u'Add liaison user: %s' % (self.person.pk, self.person.email()[1], self.person, ) - - user_name.allow_tags = True - def groups(self): - user, login_name = self.user() - return ", ".join([ group.name for group in user.groups.all()]) - person_link = admin_link('person') - sdo_link = admin_link('sdo', label='SDO') - -class LiaisonManagers(LiaisonStatementManager): - email_priority = models.IntegerField(null=True, blank=True) - def email(self): - try: - return self.person.emailaddress_set.get(priority=self.email_priority) - except ObjectDoesNotExist: - return None - class Meta: - verbose_name = 'SDO Liaison Manager' - verbose_name_plural = 'SDO Liaison Managers' - db_table = 'liaison_managers' - ordering = ('sdo__sdo_name', ) + frm = self.from_name + if self.to_group: + to = self.to_group.acronym or self.to_group.name + else: + to = self.to_name + return slugify("liaison" + " " + self.submitted.strftime("%Y-%m-%d") + " " + frm[:50] + " " + to[:50] + " " + self.title[:115]) -class SDOAuthorizedIndividual(LiaisonStatementManager): - class Meta: - verbose_name = 'SDO Authorized Individual' - verbose_name_plural = 'SDO Authorized Individuals' - -# This table is not used by any code right now. -#class LiaisonsInterim(models.Model): -# title = models.CharField(blank=True, max_length=255) -# submitter_name = models.CharField(blank=True, max_length=255) -# submitter_email = models.CharField(blank=True, max_length=255) -# submitted_date = models.DateField(null=True, blank=True) -# from_id = models.IntegerField(null=True, blank=True) -# def __str__(self): -# return self.title -# class Meta: -# db_table = 'liaisons_interim' - -class Uploads(models.Model): - file_id = models.AutoField(primary_key=True) - file_title = models.CharField(blank=True, max_length=255) - person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag') - file_extension = models.CharField(blank=True, max_length=10) - detail = models.ForeignKey(LiaisonDetail) - def __str__(self): - return self.file_title - def filename(self): - return "file%s%s" % (self.file_id, self.file_extension) - class Meta: - db_table = 'uploads' - -# empty table -#class SdoChairs(models.Model): -# sdo = models.ForeignKey(SDOs) -# person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag') -# email_priority = models.IntegerField(null=True, blank=True) -# class Meta: -# db_table = 'sdo_chairs' - -# changes done by convert-096.py:changed maxlength to max_length -# removed core -# removed edit_inline -# removed num_in_admin -# removed raw_id_admin - -if settings.USE_DB_REDESIGN_PROXY_CLASSES or hasattr(settings, "IMPORTING_FROM_OLD_SCHEMA"): - from ietf.name.models import LiaisonStatementPurposeName - from ietf.doc.models import Document - from ietf.person.models import Email - from ietf.group.models import Group - - class LiaisonStatement(models.Model): - title = models.CharField(blank=True, max_length=255) - purpose = models.ForeignKey(LiaisonStatementPurposeName) - body = models.TextField(blank=True) - deadline = models.DateField(null=True, blank=True) - - related_to = models.ForeignKey('LiaisonStatement', blank=True, null=True) - - from_group = models.ForeignKey(Group, related_name="liaisonstatement_from_set", null=True, blank=True, help_text="Sender group, if it exists") - from_name = models.CharField(max_length=255, help_text="Name of the sender body") - from_contact = models.ForeignKey(Email, blank=True, null=True) - to_group = models.ForeignKey(Group, related_name="liaisonstatement_to_set", null=True, blank=True, help_text="Recipient group, if it exists") - to_name = models.CharField(max_length=255, help_text="Name of the recipient body") - to_contact = models.CharField(blank=True, max_length=255, help_text="Contacts at recipient body") - - reply_to = models.CharField(blank=True, max_length=255) - - response_contact = models.CharField(blank=True, max_length=255) - technical_contact = models.CharField(blank=True, max_length=255) - cc = models.TextField(blank=True) - - submitted = models.DateTimeField(null=True, blank=True) - modified = models.DateTimeField(null=True, blank=True) - approved = models.DateTimeField(null=True, blank=True) - - action_taken = models.BooleanField(default=False) - - attachments = models.ManyToManyField(Document, blank=True) - - def name(self): - from django.template.defaultfilters import slugify - if self.from_group: - frm = self.from_group.acronym or self.from_group.name - else: - frm = self.from_name - if self.to_group: - to = self.to_group.acronym or self.to_group.name - else: - to = self.to_name - return slugify("liaison" + " " + self.submitted.strftime("%Y-%m-%d") + " " + frm[:50] + " " + to[:50] + " " + self.title[:115]) - - def __unicode__(self): - return self.title or "" - - LiaisonDetailOld = LiaisonDetail + def __unicode__(self): + return self.title or u"" diff --git a/ietf/liaisons/proxy.py b/ietf/liaisons/proxy.py deleted file mode 100644 index 9e1ca61bc..000000000 --- a/ietf/liaisons/proxy.py +++ /dev/null @@ -1,188 +0,0 @@ -from ietf.utils.proxy import TranslatingManager -from ietf.liaisons.models import LiaisonStatement -from ietf.doc.models import Document - -class LiaisonDetailProxy(LiaisonStatement): - objects = TranslatingManager(dict(submitted_date="submitted", - deadline_date="deadline", - to_body="to_name", - from_raw_body="from_name")) - - def from_object(self, base): - for f in base._meta.fields: - setattr(self, f.name, getattr(base, f.name)) - return self - - #detail_id = models.AutoField(primary_key=True) - @property - def detail_id(self): - return self.id - #person = models.ForeignKey(PersonOrOrgInfo, null=True, db_column='person_or_org_tag') - @property - def person(self): - return self.from_contact.person if self.from_contact else "" - #submitted_date = models.DateField(null=True, blank=True) - @property - def submitted_date(self): - return self.submitted.date() if self.submitted else None - #last_modified_date = models.DateField(null=True, blank=True) - @property - def last_modified_date(self): - return self.modified.date() if self.modified else None - #from_id = models.IntegerField(null=True, blank=True) - @property - def from_id(self): - return self.from_group_id - #to_body = models.CharField(blank=True, null=True, max_length=255) - @property - def to_body(self): - return self.to_name - #title = models.CharField(blank=True, null=True, max_length=255) # same name - #response_contact = models.CharField(blank=True, null=True, max_length=255) # same name - #technical_contact = models.CharField(blank=True, null=True, max_length=255) # same name - #purpose_text = models.TextField(blank=True, null=True, db_column='purpose') - @property - def purpose_text(self): - return "" - #body = models.TextField(blank=True,null=True) # same name - #deadline_date = models.DateField(null=True, blank=True) - @property - def deadline_date(self): - return self.deadline - #cc1 = models.TextField(blank=True, null=True) - @property - def cc1(self): - return self.cc - #cc2 = models.CharField(blank=True, null=True, max_length=50) # unused - @property - def cc2(self): - return "" - #submitter_name = models.CharField(blank=True, null=True, max_length=255) - @property - def submitter_name(self): - i = self.to_name.find('<') - if i > 0: - return self.to_name[:i - 1] - else: - return self.to_name - #submitter_email = models.CharField(blank=True, null=True, max_length=255) - @property - def submitter_email(self): - import re - re_email = re.compile("<(.*)>") - match = re_email.search(self.to_name) - if match: - return match.group(1) - else: - return "" - #by_secretariat = models.IntegerField(null=True, blank=True) - @property - def by_secretariat(self): - return not self.from_contact - #to_poc = models.CharField(blank=True, null=True, max_length=255) - @property - def to_poc(self): - return self.to_contact - #to_email = models.CharField(blank=True, null=True, max_length=255) - @property - def to_email(self): - return "" - #purpose = models.ForeignKey(LiaisonPurpose,null=True) - #replyto = models.CharField(blank=True, null=True, max_length=255) - @property - def replyto(self): - return self.reply_to - #from_raw_body = models.CharField(blank=True, null=True, max_length=255) - @property - def from_raw_body(self): - return self.from_name - - def raw_codify(self, group): - if not group: - return "" - if group.type_id in ("sdo", "wg", "area"): - return "%s_%s" % (group.type_id, group.id) - return group.acronym - - #from_raw_code = models.CharField(blank=True, null=True, max_length=255) - @property - def from_raw_code(self): - return self.raw_codify(self.from_group) - #to_raw_code = models.CharField(blank=True, null=True, max_length=255) - @property - def to_raw_code(self): - return self.raw_codify(self.to_group) - #approval = models.ForeignKey(OutgoingLiaisonApproval, blank=True, null=True) - @property - def approval(self): - return bool(self.approved) - #action_taken = models.BooleanField(default=False, db_column='taken_care') # same name - #related_to = models.ForeignKey('LiaisonDetail', blank=True, null=True) # same name - - @property - def uploads_set(self): - return UploadsProxy.objects.filter(liaisonstatement=self).order_by("name", "external_url") - - @property - def liaisondetail_set(self): - return self.liaisonstatement_set - - def __str__(self): - return unicode(self) - def __unicode__(self): - return self.title or "" - def from_body(self): - return self.from_name - def from_sdo(self): - return self.from_group if self.from_group and self.from_group.type_id == "sdo" else None - def from_email(self): - return self.from_contact.address - def get_absolute_url(self): - return '/liaison/%d/' % self.detail_id - class Meta: - proxy = True - - def send_by_email(self, fake=False): - # grab this from module instead of stuffing in on the model - from ietf.liaisons.mails import send_liaison_by_email - # we don't have a request so just pass None for the time being - return send_liaison_by_email(None, self, fake) - - def notify_pending_by_email(self, fake=False): - # grab this from module instead of stuffing in on the model - from ietf.liaisons.mails import notify_pending_by_email - # we don't have a request so just pass None for the time being - return notify_pending_by_email(None, self, fake) - - def is_pending(self): - return not self.approved - -class UploadsProxy(Document): - #file_id = models.AutoField(primary_key=True) - @property - def file_id(self): - if not self.external_url or self.external_url.startswith(self.name): - return self.name # new data - else: - return int(self.external_url.split(".")[0][len("file"):]) # old data - #file_title = models.CharField(blank=True, max_length=255) - @property - def file_title(self): - return self.title - #person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag') - #file_extension = models.CharField(blank=True, max_length=10) - @property - def file_extension(self): - t = self.external_url.split(".") - if len(t) > 1: - return "." + t[1] - else: - return "" - #detail = models.ForeignKey(LiaisonDetail) - @property - def detail(self): - return self.liaisonstatement_set.all()[0] - def filename(self): - return self.external_url - class Meta: - proxy = True diff --git a/ietf/liaisons/sitemaps.py b/ietf/liaisons/sitemaps.py index 156ffed4c..161fed1c3 100644 --- a/ietf/liaisons/sitemaps.py +++ b/ietf/liaisons/sitemaps.py @@ -2,16 +2,17 @@ # from django.contrib.sitemaps import Sitemap from django.conf import settings -from ietf.liaisons.models import LiaisonDetail -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail +from ietf.liaisons.models import LiaisonStatement class LiaisonMap(Sitemap): changefreq = "never" + def items(self): - return LiaisonDetail.objects.all() + return LiaisonStatement.objects.all() + def location(self, obj): - return "/liaison/%d/" % obj.detail_id + return "/liaison/%s/" % obj.pk + def lastmod(self, obj): - return obj.last_modified_date + return obj.modified diff --git a/ietf/liaisons/tests.py b/ietf/liaisons/tests.py index bd7da0995..e358b3176 100644 --- a/ietf/liaisons/tests.py +++ b/ietf/liaisons/tests.py @@ -11,6 +11,11 @@ from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox from ietf.utils import TestCase +from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName +from ietf.person.models import Person, Email +from ietf.group.models import Group, Role +from ietf.liaisons.mails import send_sdo_reminder, possibly_send_deadline_reminder + class LiaisonsUrlTestCase(SimpleUrlTestCase): def testUrls(self): self.doTestUrls(__file__) @@ -22,11 +27,6 @@ class LiaisonsUrlTestCase(SimpleUrlTestCase): else: return content -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName - from ietf.person.models import Person, Email - from ietf.group.models import Group, Role - def make_liaison_models(): sdo = Group.objects.create( name="United League of Marsmen", @@ -89,7 +89,58 @@ def make_liaison_models(): action_taken=False, ) return l - + +class LiaisonTests(TestCase): + def test_overview(self): + make_test_data() + liaison = make_liaison_models() + + r = self.client.get(urlreverse('liaison_list')) + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) + + def test_details(self): + make_test_data() + liaison = make_liaison_models() + + r = self.client.get(urlreverse("liaison_detail", kwargs={ 'object_id': liaison.pk })) + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) + + def test_feeds(self): + make_test_data() + liaison = make_liaison_models() + + r = self.client.get('/feed/liaison/recent/') + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) + + r = self.client.get('/feed/liaison/from/%s/' % liaison.from_group.acronym) + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) + + r = self.client.get('/feed/liaison/to/%s/' % liaison.to_name) + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) + + r = self.client.get('/feed/liaison/subject/marsmen/') + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) + + def test_sitemap(self): + make_test_data() + liaison = make_liaison_models() + + r = self.client.get('/sitemap-liaison.xml') + self.assertEqual(r.status_code, 200) + self.assertTrue(urlreverse("liaison_detail", kwargs={ 'object_id': liaison.pk }) in r.content) + + def test_help_pages(self): + self.assertEqual(self.client.get('/liaison/help/').status_code, 200) + self.assertEqual(self.client.get('/liaison/help/fields/').status_code, 200) + self.assertEqual(self.client.get('/liaison/help/from_ietf/').status_code, 200) + self.assertEqual(self.client.get('/liaison/help/to_ietf/').status_code, 200) + class LiaisonManagementTests(TestCase): def setUp(self): @@ -114,52 +165,63 @@ class LiaisonManagementTests(TestCase): url = urlreverse('liaison_detail', kwargs=dict(object_id=liaison.pk)) # normal get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=do_action_taken]')), 0) + self.assertEqual(len(q('form input[name=do_action_taken]')), 0) # log in and get self.client.login(remote_user="secretary") r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=do_action_taken]')), 1) + self.assertEqual(len(q('form input[name=do_action_taken]')), 1) # mark action taken r = self.client.post(url, dict(do_action_taken="1")) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=do_action_taken]')), 0) + self.assertEqual(len(q('form input[name=do_action_taken]')), 0) liaison = LiaisonStatement.objects.get(id=liaison.id) self.assertTrue(liaison.action_taken) - def test_approval(self): + def test_approval_process(self): make_test_data() liaison = make_liaison_models() # has to come from WG to need approval liaison.from_group = Group.objects.get(acronym="mars") liaison.approved = None liaison.save() - - url = urlreverse('liaison_approval_detail', kwargs=dict(object_id=liaison.pk)) + + # check the overview page + url = urlreverse('liaison_approval_list') # this liaison is for a WG so we need the AD for the area login_testing_unauthorized(self, "ad", url) + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) + + # check detail page + url = urlreverse('liaison_approval_detail', kwargs=dict(object_id=liaison.pk)) + self.client.logout() + login_testing_unauthorized(self, "ad", url) + # normal get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) + self.assertTrue(liaison.title in r.content) q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=do_approval]')), 1) + self.assertEqual(len(q('form input[name=do_approval]')), 1) # approve mailbox_before = len(outbox) r = self.client.post(url, dict(do_approval="1")) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) liaison = LiaisonStatement.objects.get(id=liaison.id) self.assertTrue(liaison.approved) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("Liaison Statement" in outbox[-1]["Subject"]) def test_edit_liaison(self): @@ -171,9 +233,9 @@ class LiaisonManagementTests(TestCase): # get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=from_field]')), 1) + self.assertEqual(len(q('form input[name=from_field]')), 1) # edit attachments_before = liaison.attachments.count() @@ -195,30 +257,30 @@ class LiaisonManagementTests(TestCase): attach_file_1=test_file, attach_title_1="attachment", )) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) new_liaison = LiaisonStatement.objects.get(id=liaison.id) - self.assertEquals(new_liaison.from_name, "from") - self.assertEquals(new_liaison.reply_to, "replyto@example.com") - self.assertEquals(new_liaison.to_name, "org") - self.assertEquals(new_liaison.to_contact, "to_poc@example.com") - self.assertEquals(new_liaison.response_contact, "responce_contact@example.com") - self.assertEquals(new_liaison.technical_contact, "technical_contact@example.com") - self.assertEquals(new_liaison.cc, "cc@example.com") - self.assertEquals(new_liaison.purpose, LiaisonStatementPurposeName.objects.get(order=4)) - self.assertEquals(new_liaison.deadline, liaison.deadline + datetime.timedelta(days=1)), - self.assertEquals(new_liaison.title, "title") - self.assertEquals(new_liaison.submitted.date(), (liaison.submitted + datetime.timedelta(days=1)).date()) - self.assertEquals(new_liaison.body, "body") + self.assertEqual(new_liaison.from_name, "from") + self.assertEqual(new_liaison.reply_to, "replyto@example.com") + self.assertEqual(new_liaison.to_name, "org") + self.assertEqual(new_liaison.to_contact, "to_poc@example.com") + self.assertEqual(new_liaison.response_contact, "responce_contact@example.com") + self.assertEqual(new_liaison.technical_contact, "technical_contact@example.com") + self.assertEqual(new_liaison.cc, "cc@example.com") + self.assertEqual(new_liaison.purpose, LiaisonStatementPurposeName.objects.get(order=4)) + self.assertEqual(new_liaison.deadline, liaison.deadline + datetime.timedelta(days=1)), + self.assertEqual(new_liaison.title, "title") + self.assertEqual(new_liaison.submitted.date(), (liaison.submitted + datetime.timedelta(days=1)).date()) + self.assertEqual(new_liaison.body, "body") - self.assertEquals(new_liaison.attachments.count(), attachments_before + 1) + self.assertEqual(new_liaison.attachments.count(), attachments_before + 1) attachment = new_liaison.attachments.order_by("-name")[0] - self.assertEquals(attachment.title, "attachment") + self.assertEqual(attachment.title, "attachment") with open(os.path.join(self.liaison_dir, attachment.external_url)) as f: written_content = f.read() test_file.seek(0) - self.assertEquals(written_content, test_file.read()) + self.assertEqual(written_content, test_file.read()) def test_add_incoming_liaison(self): make_test_data() @@ -229,9 +291,9 @@ class LiaisonManagementTests(TestCase): # get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('form textarea[name=body]')), 1) + self.assertEqual(len(q('form textarea[name=body]')), 1) # add new mailbox_before = len(outbox) @@ -260,34 +322,34 @@ class LiaisonManagementTests(TestCase): attach_title_1="attachment", send="1", )) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) l = LiaisonStatement.objects.all().order_by("-id")[0] - self.assertEquals(l.from_group, from_group) - self.assertEquals(l.from_contact.address, submitter.email_address()) - self.assertEquals(l.reply_to, "replyto@example.com") - self.assertEquals(l.to_group, to_group) - self.assertEquals(l.response_contact, "responce_contact@example.com") - self.assertEquals(l.technical_contact, "technical_contact@example.com") - self.assertEquals(l.cc, "cc@example.com") - self.assertEquals(l.purpose, LiaisonStatementPurposeName.objects.get(order=4)) - self.assertEquals(l.deadline, today + datetime.timedelta(days=1)), - self.assertEquals(l.related_to, liaison), - self.assertEquals(l.title, "title") - self.assertEquals(l.submitted.date(), today) - self.assertEquals(l.body, "body") + self.assertEqual(l.from_group, from_group) + self.assertEqual(l.from_contact.address, submitter.email_address()) + self.assertEqual(l.reply_to, "replyto@example.com") + self.assertEqual(l.to_group, to_group) + self.assertEqual(l.response_contact, "responce_contact@example.com") + self.assertEqual(l.technical_contact, "technical_contact@example.com") + self.assertEqual(l.cc, "cc@example.com") + self.assertEqual(l.purpose, LiaisonStatementPurposeName.objects.get(order=4)) + self.assertEqual(l.deadline, today + datetime.timedelta(days=1)), + self.assertEqual(l.related_to, liaison), + self.assertEqual(l.title, "title") + self.assertEqual(l.submitted.date(), today) + self.assertEqual(l.body, "body") self.assertTrue(l.approved) - self.assertEquals(l.attachments.count(), 1) + self.assertEqual(l.attachments.count(), 1) attachment = l.attachments.all()[0] - self.assertEquals(attachment.title, "attachment") + self.assertEqual(attachment.title, "attachment") with open(os.path.join(self.liaison_dir, attachment.external_url)) as f: written_content = f.read() test_file.seek(0) - self.assertEquals(written_content, test_file.read()) + self.assertEqual(written_content, test_file.read()) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("Liaison Statement" in outbox[-1]["Subject"]) def test_add_outgoing_liaison(self): @@ -299,9 +361,9 @@ class LiaisonManagementTests(TestCase): # get r = self.client.get(url) - self.assertEquals(r.status_code, 200) + self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('form textarea[name=body]')), 1) + self.assertEqual(len(q('form textarea[name=body]')), 1) # add new mailbox_before = len(outbox) @@ -333,35 +395,35 @@ class LiaisonManagementTests(TestCase): attach_title_1="attachment", send="1", )) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) l = LiaisonStatement.objects.all().order_by("-id")[0] - self.assertEquals(l.from_group, from_group) - self.assertEquals(l.from_contact.address, submitter.email_address()) - self.assertEquals(l.reply_to, "replyto@example.com") - self.assertEquals(l.to_group, to_group) - self.assertEquals(l.to_contact, "to_poc@example.com") - self.assertEquals(l.response_contact, "responce_contact@example.com") - self.assertEquals(l.technical_contact, "technical_contact@example.com") - self.assertEquals(l.cc, "cc@example.com") - self.assertEquals(l.purpose, LiaisonStatementPurposeName.objects.get(order=4)) - self.assertEquals(l.deadline, today + datetime.timedelta(days=1)), - self.assertEquals(l.related_to, liaison), - self.assertEquals(l.title, "title") - self.assertEquals(l.submitted.date(), today) - self.assertEquals(l.body, "body") + self.assertEqual(l.from_group, from_group) + self.assertEqual(l.from_contact.address, submitter.email_address()) + self.assertEqual(l.reply_to, "replyto@example.com") + self.assertEqual(l.to_group, to_group) + self.assertEqual(l.to_contact, "to_poc@example.com") + self.assertEqual(l.response_contact, "responce_contact@example.com") + self.assertEqual(l.technical_contact, "technical_contact@example.com") + self.assertEqual(l.cc, "cc@example.com") + self.assertEqual(l.purpose, LiaisonStatementPurposeName.objects.get(order=4)) + self.assertEqual(l.deadline, today + datetime.timedelta(days=1)), + self.assertEqual(l.related_to, liaison), + self.assertEqual(l.title, "title") + self.assertEqual(l.submitted.date(), today) + self.assertEqual(l.body, "body") self.assertTrue(not l.approved) - self.assertEquals(l.attachments.count(), 1) + self.assertEqual(l.attachments.count(), 1) attachment = l.attachments.all()[0] - self.assertEquals(attachment.title, "attachment") + self.assertEqual(attachment.title, "attachment") with open(os.path.join(self.liaison_dir, attachment.external_url)) as f: written_content = f.read() test_file.seek(0) - self.assertEquals(written_content, test_file.read()) + self.assertEqual(written_content, test_file.read()) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("Liaison Statement" in outbox[-1]["Subject"]) # try adding statement to non-predefined organization @@ -383,46 +445,34 @@ class LiaisonManagementTests(TestCase): submitted_date=today.strftime("%Y-%m-%d"), body="body", )) - self.assertEquals(r.status_code, 302) + self.assertEqual(r.status_code, 302) l = LiaisonStatement.objects.all().order_by("-id")[0] - self.assertEquals(l.to_group, None) - self.assertEquals(l.to_name, "Mars Institute") + self.assertEqual(l.to_group, None) + self.assertEqual(l.to_name, "Mars Institute") def test_send_sdo_reminder(self): make_test_data() liaison = make_liaison_models() - from ietf.liaisons.mails import send_sdo_reminder - mailbox_before = len(outbox) send_sdo_reminder(Group.objects.filter(type="sdo")[0]) - self.assertEquals(len(outbox), mailbox_before + 1) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("authorized individuals" in outbox[-1]["Subject"]) def test_send_liaison_deadline_reminder(self): make_test_data() liaison = make_liaison_models() - from ietf.liaisons.mails import possibly_send_deadline_reminder - from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail - - l = LiaisonDetail.objects.all()[0] - mailbox_before = len(outbox) - possibly_send_deadline_reminder(l) - self.assertEquals(len(outbox), mailbox_before + 1) + possibly_send_deadline_reminder(liaison) + self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue("deadline" in outbox[-1]["Subject"]) # try pushing the deadline - l.deadline = l.deadline + datetime.timedelta(days=30) - l.save() + liaison.deadline = liaison.deadline + datetime.timedelta(days=30) + liaison.save() mailbox_before = len(outbox) - possibly_send_deadline_reminder(l) - self.assertEquals(len(outbox), mailbox_before) - - -if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - # the above tests only work with the new schema - del LiaisonManagementTestCase + possibly_send_deadline_reminder(liaison) + self.assertEqual(len(outbox), mailbox_before) diff --git a/ietf/liaisons/testurl.list b/ietf/liaisons/testurl.list deleted file mode 100644 index 59ca172cd..000000000 --- a/ietf/liaisons/testurl.list +++ /dev/null @@ -1,33 +0,0 @@ -200 /liaison/ -200 /liaison/321/ -200 /liaison/553/ # submitted by email -200 /liaison/337/ # test case for ticket #182 -200 /liaison/458/ # non-ASCII title -200 /liaison/471/ # non-ASCII body and submitter name - -301 /liaison/managers/ - -200 /liaison/help/to_ietf/ -200 /liaison/help/from_ietf/ -200 /liaison/help/fields/ -200 /liaison/help/ - -404 /feed/liaison/ -200 /feed/liaison/recent/ -200 /feed/liaison/from/ccamp/ -#200 /feed/liaison/from/MFA%20Forum/ -200 /feed/liaison/from/MFA_Forum/ -200 /feed/liaison/to/ccamp/ -200 /feed/liaison/subject/H.248/ -200 /feed/liaison/subject/2173/ # non-ASCII title - -404 /feed/liaison/recent/foobar/ -404 /feed/liaison/from/ -404 /feed/liaison/from/nosuchorganization/ -404 /feed/liaison/from/foo/bar/ -404 /feed/liaison/to/ -404 /feed/liaison/to/foo/bar/ -404 /feed/liaison/subject/ -404 /feed/liaison/subject/foo/bar/ - -200 /sitemap-liaison.xml diff --git a/ietf/liaisons/urls.py b/ietf/liaisons/urls.py index a9ddb236b..f09523884 100644 --- a/ietf/liaisons/urls.py +++ b/ietf/liaisons/urls.py @@ -2,17 +2,8 @@ from django.conf.urls.defaults import patterns, url from django.db.models import Q -from ietf.liaisons.models import LiaisonDetail -info_dict = { - 'queryset': LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by("-submitted_date"), -} - -# there's an opportunity for date-based filtering. -urlpatterns = patterns('django.views.generic.list_detail', -) - -urlpatterns += patterns('django.views.generic.simple', +urlpatterns = patterns('django.views.generic.simple', (r'^help/$', 'direct_to_template', {'template': 'liaisons/help.html'}), (r'^help/fields/$', 'direct_to_template', {'template': 'liaisons/field_help.html'}), (r'^help/from_ietf/$', 'direct_to_template', {'template': 'liaisons/guide_from_ietf.html'}), diff --git a/ietf/liaisons/utils.py b/ietf/liaisons/utils.py index 0d8b48c59..ec5d2a2c7 100644 --- a/ietf/liaisons/utils.py +++ b/ietf/liaisons/utils.py @@ -1,10 +1,43 @@ -from django.conf import settings +from django.db.models import Q -from ietf.idtracker.models import Area, IETFWG -from ietf.liaisons.models import SDOs, LiaisonManagers -from ietf.liaisons.accounts import (is_ietfchair, is_iabchair, is_iab_executive_director, is_irtfchair, +from ietf.group.models import Group, Role +from ietf.person.models import Person +from ietf.liaisons.models import LiaisonStatement +from ietf.ietfauth.utils import has_role, passes_test_decorator +from ietf.utils.proxy import proxy_personify_role + +from ietf.liaisons.accounts import (is_ietfchair, is_iabchair, is_iab_executive_director, get_ietf_chair, get_iab_chair, get_iab_executive_director, - is_secretariat) + is_secretariat, can_add_liaison, get_person_for_user) + +can_submit_liaison_required = passes_test_decorator( + lambda u, *args, **kwargs: can_add_liaison(u), + "Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities") + +def approvable_liaison_statements(user): + liaisons = LiaisonStatement.objects.filter(approved=None) + if has_role(user, "Secretariat"): + return liaisons + + # this is a bit complicated because IETFHM encodes the + # groups, it should just give us a list of ids or acronyms + group_codes = IETFHM.get_all_can_approve_codes(get_person_for_user(user)) + group_acronyms = [] + group_ids = [] + for x in group_codes: + if "_" in x: + group_ids.append(x.split("_")[1]) + else: + group_acronyms.append(x) + + return liaisons.filter(Q(from_group__acronym__in=group_acronyms) | Q(from_group__pk__in=group_ids)) + + +# the following is a biggish object hierarchy abstracting the entity +# names and auth rules for posting liaison statements in a sort of +# semi-declarational (and perhaps overengineered given the revamped +# schema) way - unfortunately, it's never been strong enough to do so +# fine-grained enough so the form code also has some rules IETFCHAIR = {'name': u'The IETF Chair', 'address': u'chair@ietf.org'} IESG = {'name': u'The IESG', 'address': u'iesg@ietf.org'} @@ -12,10 +45,7 @@ IAB = {'name': u'The IAB', 'address': u'iab@iab.org'} IABCHAIR = {'name': u'The IAB Chair', 'address': u'iab-chair@iab.org'} IABEXECUTIVEDIRECTOR = {'name': u'The IAB Executive Director', 'address': u'execd@iab.org'} IRTFCHAIR = {'name': u'The IRTF Chair', 'address': u'irtf-chair@irtf.org'} - - -def get_all_sdo_managers(): - return [i.person for i in LiaisonManagers.objects.all().distinct()] +IESGANDIAB = {'name': u'The IESG and IAB', 'address': u'iesg-iab@ietf.org'} class FakePerson(object): @@ -27,7 +57,12 @@ class FakePerson(object): def email(self): return (self.name, self.address) +def all_sdo_managers(): + return [proxy_personify_role(r) for r in Role.objects.filter(group__type="sdo", name="liaiman").select_related("person").distinct()] +def role_persons_with_fixed_email(group, role_name): + return [proxy_personify_role(r) for r in Role.objects.filter(group=group, name=role_name).select_related("person").distinct()] + class Entity(object): poc = [] @@ -84,34 +119,11 @@ class IETFEntity(Entity): return [self.poc] def full_user_list(self): - result = get_all_sdo_managers() + result = all_sdo_managers() result.append(get_ietf_chair()) return result -class IRTFEntity(Entity): - - poc = FakePerson(**IRTFCHAIR) - - def get_from_cc(self, person): - result = [] - if not is_irtfchair(person): - result.append(self.poc) - return result - - def needs_approval(self, person=None): - if is_irtfchair(person): - return False - return True - - def can_approve(self): - return [self.poc] - - def full_user_list(self): - result.append(get_irtf_chair()) - return result - - class IABEntity(Entity): chair = FakePerson(**IABCHAIR) director = FakePerson(**IABEXECUTIVEDIRECTOR) @@ -136,27 +148,75 @@ class IABEntity(Entity): return [self.chair] def full_user_list(self): - result = get_all_sdo_managers() + result = all_sdo_managers() result += [get_iab_chair(), get_iab_executive_director()] return result +class IRTFEntity(Entity): + chair = FakePerson(**IRTFCHAIR) + poc = [chair,] + + def get_from_cc(self, person): + result = [] + return result + + def needs_approval(self, person=None): + if is_irtfchair(person): + return False + return True + + def can_approve(self): + return [self.chair] + + def full_user_list(self): + result = [get_irtf_chair()] + return result + + +class IAB_IESG_Entity(Entity): + + poc = [IABEntity.chair, IABEntity.director, FakePerson(**IETFCHAIR), FakePerson(**IESGANDIAB), ] + cc = [FakePerson(**IAB), FakePerson(**IESG), FakePerson(**IESGANDIAB)] + + def __init__(self, name, obj=None): + self.name = name + self.obj = obj + self.iab = IABEntity(name, obj) + self.iesg = IETFEntity(name, obj) + + def get_from_cc(self, person): + return list(set(self.iab.get_from_cc(person) + self.iesg.get_from_cc(person))) + + def needs_approval(self, person=None): + if not self.iab.needs_approval(person): + return False + if not self.iesg.needs_approval(person): + return False + return True + + def can_approve(self): + return list(set(self.iab.can_approve() + self.iesg.can_approve())) + + def full_user_list(self): + return [get_ietf_chair(), get_iab_chair(), get_iab_executive_director()] + class AreaEntity(Entity): def get_poc(self): - return [i.person for i in self.obj.areadirector_set.all()] + return role_persons_with_fixed_email(self.obj, "ad") def get_cc(self, person=None): return [FakePerson(**IETFCHAIR)] def get_from_cc(self, person): - result = [i.person for i in self.obj.areadirector_set.all() if i.person!=person] + result = [p for p in role_persons_with_fixed_email(self.obj, "ad") if p != person] result.append(FakePerson(**IETFCHAIR)) return result def needs_approval(self, person=None): # Check if person is an area director - if self.obj.areadirector_set.filter(person=person): + if self.obj.role_set.filter(person=person, name="ad"): return False return True @@ -164,7 +224,7 @@ class AreaEntity(Entity): return self.get_poc() def full_user_list(self): - result = get_all_sdo_managers() + result = all_sdo_managers() result += self.get_poc() return result @@ -172,34 +232,37 @@ class AreaEntity(Entity): class WGEntity(Entity): def get_poc(self): - return [i.person for i in self.obj.wgchair_set.all()] + return role_persons_with_fixed_email(self.obj, "chair") def get_cc(self, person=None): - result = [i.person for i in self.obj.area_directors()] - if self.obj.email_address: - result.append(FakePerson(name ='%s Discussion List' % self.obj.group_acronym.name, - address = self.obj.email_address)) + if self.obj.parent: + result = [p for p in role_persons_with_fixed_email(self.obj.parent, "ad") if p != person] + else: + result = [] + if self.obj.list_subscribe: + result.append(FakePerson(name ='%s Discussion List' % self.obj.name, + address = self.obj.list_subscribe)) return result def get_from_cc(self, person): - result = [i.person for i in self.obj.wgchair_set.all() if i.person!=person] - result += [i.person for i in self.obj.area_directors()] - if self.obj.email_address: - result.append(FakePerson(name ='%s Discussion List' % self.obj.group_acronym.name, - address = self.obj.email_address)) + result = [p for p in role_persons_with_fixed_email(self.obj, "chair") if p != person] + result += role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else [] + if self.obj.list_subscribe: + result.append(FakePerson(name ='%s Discussion List' % self.obj.name, + address = self.obj.list_subscribe)) return result def needs_approval(self, person=None): # Check if person is director of this wg area - if self.obj.area.area.areadirector_set.filter(person=person): + if self.obj.parent and self.obj.parent.role_set.filter(person=person, name="ad"): return False return True def can_approve(self): - return [i.person for i in self.obj.area.area.areadirector_set.all()] + return role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else [] def full_user_list(self): - result = get_all_sdo_managers() + result = all_sdo_managers() result += self.get_poc() return result @@ -210,25 +273,19 @@ class SDOEntity(Entity): return [] def get_cc(self, person=None): - manager = self.obj.liaisonmanager() - if manager: - return [manager.person] - return [] + return role_persons_with_fixed_email(self.obj, "liaiman") def get_from_cc(self, person=None): - manager = self.obj.liaisonmanager() - if manager and manager.person!=person: - return [manager.person] - return [] + return [p for p in role_persons_with_fixed_email(self.obj, "liaiman") if p != person] def post_only(self, person, user): - if is_secretariat(user) or person.sdoauthorizedindividual_set.filter(sdo=self.obj): + if is_secretariat(user) or self.obj.role_set.filter(person=person, name="auth"): return False return True def full_user_list(self): - result = [i.person for i in self.obj.liaisonmanagers_set.all().distinct()] - result += [i.person for i in self.obj.sdoauthorizedindividual_set.all().distinct()] + result = role_persons_with_fixed_email(self.obj, "liaiman") + result += role_persons_with_fixed_email(self.obj, "auth") return result @@ -314,10 +371,35 @@ class IRTFEntityManager(EntityManager): return [] +class IAB_IESG_EntityManager(EntityManager): + + def __init__(self, *args, **kwargs): + super(IAB_IESG_EntityManager, self).__init__(*args, **kwargs) + self.entity = IAB_IESG_Entity(name=self.name) + + def get_entity(self, pk=None): + return self.entity + + def can_send_on_behalf(self, person): + if (is_iabchair(person) or + is_iab_executive_director(person) or + is_ietfchair(person)): + return self.get_managed_list() + return [] + + def can_approve_list(self, person): + if (is_iabchair(person) or + is_iab_executive_director(person) or + is_ietfchair(person)): + return self.get_managed_list() + return [] + + class AreaEntityManager(EntityManager): def __init__(self, pk=None, name=None, queryset=None): super(AreaEntityManager, self).__init__(pk, name, queryset) + from ietf.group.proxy import Area if self.queryset == None: self.queryset = Area.active_areas() @@ -336,11 +418,11 @@ class AreaEntityManager(EntityManager): return AreaEntity(name=obj.area_acronym.name, obj=obj) def can_send_on_behalf(self, person): - query_filter = {'areadirector__in': person.areadirector_set.all()} + query_filter = dict(role__person=person, role__name="ad") return self.get_managed_list(query_filter) def can_approve_list(self, person): - query_filter = {'areadirector__in': person.areadirector_set.all()} + query_filter = dict(role__person=person, role__name="ad") return self.get_managed_list(query_filter) @@ -349,6 +431,7 @@ class WGEntityManager(EntityManager): def __init__(self, pk=None, name=None, queryset=None): super(WGEntityManager, self).__init__(pk, name, queryset) if self.queryset == None: + from ietf.group.proxy import IETFWG, Area self.queryset = IETFWG.objects.filter(group_type=1, status=IETFWG.ACTIVE, areagroup__area__status=Area.ACTIVE) def get_managed_list(self, query_filter=None): @@ -366,13 +449,12 @@ class WGEntityManager(EntityManager): return WGEntity(name=obj.group_acronym.name, obj=obj) def can_send_on_behalf(self, person): - wgs = set([i.group_acronym.pk for i in person.wgchair_set.all()]) - wgs = wgs.union([i.group_acronym.pk for i in person.wgsecretary_set.all()]) + wgs = Group.objects.filter(role__person=person, role__name__in=("chair", "secretary")).values_list('pk', flat=True) query_filter = {'pk__in': wgs} return self.get_managed_list(query_filter) def can_approve_list(self, person): - query_filter = {'areagroup__area__areadirector__in': person.areadirector_set.all()} + query_filter = dict(parent__role__person=person, parent__role__name="ad") return self.get_managed_list(query_filter) @@ -381,10 +463,10 @@ class SDOEntityManager(EntityManager): def __init__(self, pk=None, name=None, queryset=None): super(SDOEntityManager, self).__init__(pk, name, queryset) if self.queryset == None: - self.queryset = SDOs.objects.all() + self.queryset = Group.objects.filter(type="sdo") def get_managed_list(self): - return [(u'%s_%s' % (self.pk, i.pk), i.sdo_name) for i in self.queryset.order_by('sdo_name')] + return [(u'%s_%s' % (self.pk, i.pk), i.name) for i in self.queryset.order_by('name')] def get_entity(self, pk=None): if not pk: @@ -393,7 +475,7 @@ class SDOEntityManager(EntityManager): obj = self.queryset.get(pk=pk) except self.queryset.model.DoesNotExist: return None - return SDOEntity(name=obj.sdo_name, obj=obj) + return SDOEntity(name=obj.name, obj=obj) class IETFHierarchyManager(object): @@ -402,7 +484,7 @@ class IETFHierarchyManager(object): self.managers = {'ietf': IETFEntityManager(pk='ietf', name=u'The IETF'), 'iesg': IETFEntityManager(pk='iesg', name=u'The IESG'), 'iab': IABEntityManager(pk='iab', name=u'The IAB'), - 'irtf': IRTFEntityManager(pk='irtf', name=u'The IAB'), + 'iabiesg': IAB_IESG_EntityManager(pk='iabiesg', name=u'The IESG and the IAB'), 'area': AreaEntityManager(pk='area', name=u'IETF Areas'), 'wg': WGEntityManager(pk='wg', name=u'IETF Working Groups'), 'sdo': SDOEntityManager(pk='sdo', name=u'Standards Development Organizations'), @@ -430,7 +512,7 @@ class IETFHierarchyManager(object): def get_all_incoming_entities(self): entities = [] results = [] - for key in ['ietf', 'iesg', 'iab']: + for key in ['ietf', 'iesg', 'iab', 'iabiesg']: results += self.managers[key].get_managed_list() entities.append(('Main IETF Entities', results)) entities.append(('IETF Areas', self.managers['area'].get_managed_list())) @@ -445,7 +527,7 @@ class IETFHierarchyManager(object): def get_entities_for_person(self, person): entities = [] results = [] - for key in ['ietf', 'iesg', 'iab']: + for key in ['ietf', 'iesg', 'iab', 'iabiesg']: results += self.managers[key].can_send_on_behalf(person) if results: entities.append(('Main IETF Entities', results)) @@ -459,7 +541,7 @@ class IETFHierarchyManager(object): def get_all_can_approve_codes(self, person): entities = [] - for key in ['ietf', 'iesg', 'iab']: + for key in ['ietf', 'iesg', 'iab', 'iabiesg']: entities += self.managers[key].can_approve_list(person) entities += self.managers['area'].can_approve_list(person) entities += self.managers['wg'].can_approve_list(person) @@ -467,6 +549,3 @@ class IETFHierarchyManager(object): IETFHM = IETFHierarchyManager() - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from utilsREDESIGN import * diff --git a/ietf/liaisons/utilsREDESIGN.py b/ietf/liaisons/utilsREDESIGN.py deleted file mode 100644 index 53668003c..000000000 --- a/ietf/liaisons/utilsREDESIGN.py +++ /dev/null @@ -1,524 +0,0 @@ -from ietf.group.models import Group, Role -from ietf.person.models import Person -from ietf.utils.proxy import proxy_personify_role - -from ietf.liaisons.accounts import (is_ietfchair, is_iabchair, is_iab_executive_director, - get_ietf_chair, get_iab_chair, get_iab_executive_director, - is_secretariat) - -IETFCHAIR = {'name': u'The IETF Chair', 'address': u'chair@ietf.org'} -IESG = {'name': u'The IESG', 'address': u'iesg@ietf.org'} -IAB = {'name': u'The IAB', 'address': u'iab@iab.org'} -IABCHAIR = {'name': u'The IAB Chair', 'address': u'iab-chair@iab.org'} -IABEXECUTIVEDIRECTOR = {'name': u'The IAB Executive Director', 'address': u'execd@iab.org'} -IRTFCHAIR = {'name': u'The IRTF Chair', 'address': u'irtf-chair@irtf.org'} -IESGANDIAB = {'name': u'The IESG and IAB', 'address': u'iesg-iab@ietf.org'} - - -class FakePerson(object): - - def __init__(self, name, address): - self.name = name - self.address = address - - def email(self): - return (self.name, self.address) - -# the following is a biggish object hierarchy abstracting the entity -# names and auth rules for posting liaison statements in a sort of -# semi-declarational (and perhaps overengineered given the revamped -# schema) way - unfortunately, it's never been strong enough to do so -# fine-grained enough so the form code also has some rules - -def all_sdo_managers(): - return [proxy_personify_role(r) for r in Role.objects.filter(group__type="sdo", name="liaiman").select_related("person").distinct()] - -def role_persons_with_fixed_email(group, role_name): - return [proxy_personify_role(r) for r in Role.objects.filter(group=group, name=role_name).select_related("person").distinct()] - -class Entity(object): - - poc = [] - cc = [] - - def __init__(self, name, obj=None): - self.name = name - self.obj = obj - - def get_poc(self): - if not isinstance(self.poc, list): - return [self.poc] - return self.poc - - def get_cc(self, person=None): - if not isinstance(self.cc, list): - return [self.cc] - return self.cc - - def get_from_cc(self, person=None): - return [] - - def needs_approval(self, person=None): - return False - - def can_approve(self): - return [] - - def post_only(self, person, user): - return False - - def full_user_list(self): - return False - - -class IETFEntity(Entity): - - poc = FakePerson(**IETFCHAIR) - cc = FakePerson(**IESG) - - def get_from_cc(self, person): - result = [] - if not is_ietfchair(person): - result.append(self.poc) - result.append(self.cc) - return result - - def needs_approval(self, person=None): - if is_ietfchair(person): - return False - return True - - def can_approve(self): - return [self.poc] - - def full_user_list(self): - result = all_sdo_managers() - result.append(get_ietf_chair()) - return result - - -class IABEntity(Entity): - chair = FakePerson(**IABCHAIR) - director = FakePerson(**IABEXECUTIVEDIRECTOR) - poc = [chair, director] - cc = FakePerson(**IAB) - - def get_from_cc(self, person): - result = [] - if not is_iabchair(person): - result.append(self.chair) - result.append(self.cc) - if not is_iab_executive_director(person): - result.append(self.director) - return result - - def needs_approval(self, person=None): - if is_iabchair(person) or is_iab_executive_director(person): - return False - return True - - def can_approve(self): - return [self.chair] - - def full_user_list(self): - result = all_sdo_managers() - result += [get_iab_chair(), get_iab_executive_director()] - return result - - -class IRTFEntity(Entity): - chair = FakePerson(**IRTFCHAIR) - poc = [chair,] - - def get_from_cc(self, person): - result = [] - return result - - def needs_approval(self, person=None): - if is_irtfchair(person): - return False - return True - - def can_approve(self): - return [self.chair] - - def full_user_list(self): - result = [get_irtf_chair()] - return result - - -class IAB_IESG_Entity(Entity): - - poc = [IABEntity.chair, IABEntity.director, FakePerson(**IETFCHAIR), FakePerson(**IESGANDIAB), ] - cc = [FakePerson(**IAB), FakePerson(**IESG), FakePerson(**IESGANDIAB)] - - def __init__(self, name, obj=None): - self.name = name - self.obj = obj - self.iab = IABEntity(name, obj) - self.iesg = IETFEntity(name, obj) - - def get_from_cc(self, person): - return list(set(self.iab.get_from_cc(person) + self.iesg.get_from_cc(person))) - - def needs_approval(self, person=None): - if not self.iab.needs_approval(person): - return False - if not self.iesg.needs_approval(person): - return False - return True - - def can_approve(self): - return list(set(self.iab.can_approve() + self.iesg.can_approve())) - - def full_user_list(self): - return [get_ietf_chair(), get_iab_chair(), get_iab_executive_director()] - -class AreaEntity(Entity): - - def get_poc(self): - return role_persons_with_fixed_email(self.obj, "ad") - - def get_cc(self, person=None): - return [FakePerson(**IETFCHAIR)] - - def get_from_cc(self, person): - result = [p for p in role_persons_with_fixed_email(self.obj, "ad") if p != person] - result.append(FakePerson(**IETFCHAIR)) - return result - - def needs_approval(self, person=None): - # Check if person is an area director - if self.obj.role_set.filter(person=person, name="ad"): - return False - return True - - def can_approve(self): - return self.get_poc() - - def full_user_list(self): - result = all_sdo_managers() - result += self.get_poc() - return result - - -class WGEntity(Entity): - - def get_poc(self): - return role_persons_with_fixed_email(self.obj, "chair") - - def get_cc(self, person=None): - if self.obj.parent: - result = [p for p in role_persons_with_fixed_email(self.obj.parent, "ad") if p != person] - else: - result = [] - if self.obj.list_subscribe: - result.append(FakePerson(name ='%s Discussion List' % self.obj.name, - address = self.obj.list_subscribe)) - return result - - def get_from_cc(self, person): - result = [p for p in role_persons_with_fixed_email(self.obj, "chair") if p != person] - result += role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else [] - if self.obj.list_subscribe: - result.append(FakePerson(name ='%s Discussion List' % self.obj.name, - address = self.obj.list_subscribe)) - return result - - def needs_approval(self, person=None): - # Check if person is director of this wg area - if self.obj.parent and self.obj.parent.role_set.filter(person=person, name="ad"): - return False - return True - - def can_approve(self): - return role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else [] - - def full_user_list(self): - result = all_sdo_managers() - result += self.get_poc() - return result - - -class SDOEntity(Entity): - - def get_poc(self): - return [] - - def get_cc(self, person=None): - return role_persons_with_fixed_email(self.obj, "liaiman") - - def get_from_cc(self, person=None): - return [p for p in role_persons_with_fixed_email(self.obj, "liaiman") if p != person] - - def post_only(self, person, user): - if is_secretariat(user) or self.obj.role_set.filter(person=person, name="auth"): - return False - return True - - def full_user_list(self): - result = role_persons_with_fixed_email(self.obj, "liaiman") - result += role_persons_with_fixed_email(self.obj, "auth") - return result - - -class EntityManager(object): - - def __init__(self, pk=None, name=None, queryset=None): - self.pk = pk - self.name = name - self.queryset = queryset - - def get_entity(self, pk=None): - return Entity(name=self.name) - - def get_managed_list(self): - return [(self.pk, self.name)] - - def can_send_on_behalf(self, person): - return [] - - def can_approve_list(self, person): - return [] - - -class IETFEntityManager(EntityManager): - - def __init__(self, *args, **kwargs): - super(IETFEntityManager, self).__init__(*args, **kwargs) - self.entity = IETFEntity(name=self.name) - - def get_entity(self, pk=None): - return self.entity - - def can_send_on_behalf(self, person): - if is_ietfchair(person): - return self.get_managed_list() - return [] - - def can_approve_list(self, person): - if is_ietfchair(person): - return self.get_managed_list() - return [] - - -class IABEntityManager(EntityManager): - - def __init__(self, *args, **kwargs): - super(IABEntityManager, self).__init__(*args, **kwargs) - self.entity = IABEntity(name=self.name) - - def get_entity(self, pk=None): - return self.entity - - def can_send_on_behalf(self, person): - if (is_iabchair(person) or - is_iab_executive_director(person)): - return self.get_managed_list() - return [] - - def can_approve_list(self, person): - if (is_iabchair(person) or - is_iab_executive_director(person)): - return self.get_managed_list() - return [] - - -class IRTFEntityManager(EntityManager): - - def __init__(self, *args, **kwargs): - super(IRTFEntityManager, self).__init__(*args, **kwargs) - self.entity = IRTFEntity(name=self.name) - - def get_entity(self, pk=None): - return self.entity - - def can_send_on_behalf(self, person): - if is_irtfchair(person): - return self.get_managed_list() - return [] - - def can_approve_list(self, person): - if is_irtfchair(person): - return self.get_managed_list() - return [] - - -class IAB_IESG_EntityManager(EntityManager): - - def __init__(self, *args, **kwargs): - super(IAB_IESG_EntityManager, self).__init__(*args, **kwargs) - self.entity = IAB_IESG_Entity(name=self.name) - - def get_entity(self, pk=None): - return self.entity - - def can_send_on_behalf(self, person): - if (is_iabchair(person) or - is_iab_executive_director(person) or - is_ietfchair(person)): - return self.get_managed_list() - return [] - - def can_approve_list(self, person): - if (is_iabchair(person) or - is_iab_executive_director(person) or - is_ietfchair(person)): - return self.get_managed_list() - return [] - - -class AreaEntityManager(EntityManager): - - def __init__(self, pk=None, name=None, queryset=None): - super(AreaEntityManager, self).__init__(pk, name, queryset) - from ietf.group.proxy import Area - if self.queryset == None: - self.queryset = Area.active_areas() - - def get_managed_list(self, query_filter=None): - if not query_filter: - query_filter = {} - return [(u'%s_%s' % (self.pk, i.pk), i.area_acronym.name) for i in self.queryset.filter(**query_filter).order_by('area_acronym__name')] - - def get_entity(self, pk=None): - if not pk: - return None - try: - obj = self.queryset.get(pk=pk) - except self.queryset.model.DoesNotExist: - return None - return AreaEntity(name=obj.area_acronym.name, obj=obj) - - def can_send_on_behalf(self, person): - query_filter = dict(role__person=person, role__name="ad") - return self.get_managed_list(query_filter) - - def can_approve_list(self, person): - query_filter = dict(role__person=person, role__name="ad") - return self.get_managed_list(query_filter) - - -class WGEntityManager(EntityManager): - - def __init__(self, pk=None, name=None, queryset=None): - super(WGEntityManager, self).__init__(pk, name, queryset) - if self.queryset == None: - from ietf.group.proxy import IETFWG, Area - self.queryset = IETFWG.objects.filter(group_type=1, status=IETFWG.ACTIVE, areagroup__area__status=Area.ACTIVE) - - def get_managed_list(self, query_filter=None): - if not query_filter: - query_filter = {} - return [(u'%s_%s' % (self.pk, i.pk), '%s - %s' % (i.group_acronym.acronym, i.group_acronym.name)) for i in self.queryset.filter(**query_filter).order_by('group_acronym__acronym')] - - def get_entity(self, pk=None): - if not pk: - return None - try: - obj = self.queryset.get(pk=pk) - except self.queryset.model.DoesNotExist: - return None - return WGEntity(name=obj.group_acronym.name, obj=obj) - - def can_send_on_behalf(self, person): - wgs = Group.objects.filter(role__person=person, role__name__in=("chair", "secretary")).values_list('pk', flat=True) - query_filter = {'pk__in': wgs} - return self.get_managed_list(query_filter) - - def can_approve_list(self, person): - query_filter = dict(parent__role__person=person, parent__role__name="ad") - return self.get_managed_list(query_filter) - - -class SDOEntityManager(EntityManager): - - def __init__(self, pk=None, name=None, queryset=None): - super(SDOEntityManager, self).__init__(pk, name, queryset) - if self.queryset == None: - self.queryset = Group.objects.filter(type="sdo") - - def get_managed_list(self): - return [(u'%s_%s' % (self.pk, i.pk), i.name) for i in self.queryset.order_by('name')] - - def get_entity(self, pk=None): - if not pk: - return None - try: - obj = self.queryset.get(pk=pk) - except self.queryset.model.DoesNotExist: - return None - return SDOEntity(name=obj.name, obj=obj) - - -class IETFHierarchyManager(object): - - def __init__(self): - self.managers = {'ietf': IETFEntityManager(pk='ietf', name=u'The IETF'), - 'iesg': IETFEntityManager(pk='iesg', name=u'The IESG'), - 'iab': IABEntityManager(pk='iab', name=u'The IAB'), - 'iabiesg': IAB_IESG_EntityManager(pk='iabiesg', name=u'The IESG and the IAB'), - 'area': AreaEntityManager(pk='area', name=u'IETF Areas'), - 'wg': WGEntityManager(pk='wg', name=u'IETF Working Groups'), - 'sdo': SDOEntityManager(pk='sdo', name=u'Standards Development Organizations'), - 'othersdo': EntityManager(pk='othersdo', name=u'Other SDOs'), - } - - def get_entity_by_key(self, entity_id): - if not entity_id: - return None - id_list = entity_id.split('_', 1) - key = id_list[0] - pk = None - if len(id_list)==2: - pk = id_list[1] - if key not in self.managers.keys(): - return None - return self.managers[key].get_entity(pk) - - def get_all_entities(self): - entities = [] - for manager in self.managers.values(): - entities += manager.get_managed_list() - return entities - - def get_all_incoming_entities(self): - entities = [] - results = [] - for key in ['ietf', 'iesg', 'iab', 'iabiesg']: - results += self.managers[key].get_managed_list() - entities.append(('Main IETF Entities', results)) - entities.append(('IETF Areas', self.managers['area'].get_managed_list())) - entities.append(('IETF Working Groups', self.managers['wg'].get_managed_list())) - return entities - - def get_all_outgoing_entities(self): - entities = [(self.managers['sdo'].name, self.managers['sdo'].get_managed_list())] - entities += [(self.managers['othersdo'].name, self.managers['othersdo'].get_managed_list())] - return entities - - def get_entities_for_person(self, person): - entities = [] - results = [] - for key in ['ietf', 'iesg', 'iab', 'iabiesg']: - results += self.managers[key].can_send_on_behalf(person) - if results: - entities.append(('Main IETF Entities', results)) - areas = self.managers['area'].can_send_on_behalf(person) - if areas: - entities.append(('IETF Areas', areas)) - wgs = self.managers['wg'].can_send_on_behalf(person) - if wgs: - entities.append(('IETF Working Groups', wgs)) - return entities - - def get_all_can_approve_codes(self, person): - entities = [] - for key in ['ietf', 'iesg', 'iab', 'iabiesg']: - entities += self.managers[key].can_approve_list(person) - entities += self.managers['area'].can_approve_list(person) - entities += self.managers['wg'].can_approve_list(person) - return [i[0] for i in entities] - - -IETFHM = IETFHierarchyManager() diff --git a/ietf/liaisons/views.py b/ietf/liaisons/views.py index b935d86a9..1b8325973 100644 --- a/ietf/liaisons/views.py +++ b/ietf/liaisons/views.py @@ -4,52 +4,49 @@ from email.utils import parseaddr from django.conf import settings from django.core.urlresolvers import reverse -from django.db.models import Q -from django.core.validators import email_re +from django.core.validators import validate_email, ValidationError from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden -from django.shortcuts import render_to_response, get_object_or_404 +from django.shortcuts import render_to_response, get_object_or_404, redirect from django.template import RequestContext from django.utils import simplejson from django.views.generic.list_detail import object_list, object_detail +from ietf.liaisons.models import LiaisonStatement from ietf.liaisons.accounts import (get_person_for_user, can_add_outgoing_liaison, can_add_incoming_liaison, LIAISON_EDIT_GROUPS, is_ietfchair, is_iabchair, is_iab_executive_director, can_edit_liaison, is_secretariat) -from ietf.liaisons.decorators import can_submit_liaison from ietf.liaisons.forms import liaison_form_factory -from ietf.liaisons.models import LiaisonDetail, OutgoingLiaisonApproval -from ietf.liaisons.utils import IETFHM - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail +from ietf.liaisons.utils import IETFHM, can_submit_liaison_required, approvable_liaison_statements +from ietf.liaisons.mails import notify_pending_by_email, send_liaison_by_email -@can_submit_liaison + +@can_submit_liaison_required def add_liaison(request, liaison=None): if request.method == 'POST': form = liaison_form_factory(request, data=request.POST.copy(), files = request.FILES, liaison=liaison) if form.is_valid(): liaison = form.save() - if request.POST.get('send', None): - if liaison.is_pending(): - liaison.notify_pending_by_email() + if request.POST.get('send', False): + if not liaison.approved: + notify_pending_by_email(request, liaison) else: - liaison.send_by_email() + send_liaison_by_email(request, liaison) return HttpResponseRedirect(reverse('liaison_list')) else: form = liaison_form_factory(request, liaison=liaison) return render_to_response( - 'liaisons/liaisondetail_edit.html', + 'liaisons/edit.html', {'form': form, 'liaison': liaison}, context_instance=RequestContext(request), ) -@can_submit_liaison +@can_submit_liaison_required def get_info(request): person = get_person_for_user(request.user) @@ -74,143 +71,84 @@ def get_info(request): result.update({'error': '\n'.join([to_error, from_error])}) else: result.update({'error': False, - 'cc': [i.email() for i in to_entity.get_cc(person=person)] +\ - [i.email() for i in from_entity.get_from_cc(person=person)], + 'cc': ([i.email() for i in to_entity.get_cc(person=person)] + + [i.email() for i in from_entity.get_from_cc(person=person)]), 'poc': [i.email() for i in to_entity.get_poc()], 'needs_approval': from_entity.needs_approval(person=person), 'post_only': from_entity.post_only(person=person, user=request.user)}) if is_secretariat(request.user): full_list = [(i.pk, i.email()) for i in set(from_entity.full_user_list())] - full_list.sort(lambda x,y: cmp(x[1], y[1])) + full_list.sort(key=lambda x: x[1]) full_list = [(person.pk, person.email())] + full_list result.update({'full_list': full_list}) json_result = simplejson.dumps(result) return HttpResponse(json_result, mimetype='text/javascript') +def normalize_sort(request): + sort = request.GET.get('sort', "") + if sort not in ('submitted', 'deadline', 'title', 'to_name', 'from_name'): + sort = "submitted" -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - def approvable_liaison_statements(group_codes): - # this is a bit complicated because IETFHM encodes the - # groups, it should just give us a list of ids or acronyms - group_acronyms = [] - group_ids = [] - for x in group_codes: - if "_" in x: - group_ids.append(x.split("_")[1]) - else: - group_acronyms.append(x) + # reverse dates + order_by = "-" + sort if sort in ("submitted", "deadline") else sort - return LiaisonDetail.objects.filter(approved=None).filter(Q(from_group__acronym__in=group_acronyms) | Q (from_group__pk__in=group_ids)) + return sort, order_by def liaison_list(request): - user = request.user - can_send_outgoing = can_add_outgoing_liaison(user) - can_send_incoming = can_add_incoming_liaison(user) - can_approve = False - can_edit = False + sort, order_by = normalize_sort(request) + liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by) - person = get_person_for_user(request.user) - if person: - approval_codes = IETFHM.get_all_can_approve_codes(person) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if is_secretariat(request.user): - can_approve = LiaisonDetail.objects.filter(approved=None).order_by("-submitted").count() - else: - can_approve = approvable_liaison_statements(approval_codes).count() - else: - can_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).count() + can_send_outgoing = can_add_outgoing_liaison(request.user) + can_send_incoming = can_add_incoming_liaison(request.user) - order = request.GET.get('order_by', 'submitted_date') - plain_order = order - reverse_order = order.startswith('-') - if reverse_order: - plain_order = order[1:] - if plain_order not in ('submitted_date', 'deadline_date', 'title', 'to_body', 'from_raw_body'): - order = 'submitted_date' - reverse_order = True - plain_order = 'submitted_date' - elif plain_order in ('submitted_date', 'deadline_date'): - # Reverse order for date fields, humans find it more natural - if reverse_order: - order = plain_order - else: - order = '-%s' % plain_order - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by(order) - else: - public_liaisons = LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by(order) + approvable = approvable_liaison_statements(request.user).count() - return object_list(request, public_liaisons, - allow_empty=True, - template_name='liaisons/liaisondetail_list.html', - extra_context={'can_manage': can_approve or can_send_incoming or can_send_outgoing, - 'can_approve': can_approve, - 'can_edit': can_edit, - 'can_send_incoming': can_send_incoming, - 'can_send_outgoing': can_send_outgoing, - plain_order: not reverse_order and '-' or None}, - ) + return render_to_response('liaisons/overview.html', { + "liaisons": liaisons, + "can_manage": approvable or can_send_incoming or can_send_outgoing, + "approvable": approvable, + "can_send_incoming": can_send_incoming, + "can_send_outgoing": can_send_outgoing, + "sort": sort, + }, context_instance=RequestContext(request)) +def ajax_liaison_list(request): + sort, order_by = normalize_sort(request) + liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by) -@can_submit_liaison + return render_to_response('liaisons/liaison_table.html', { + "liaisons": liaisons, + "sort": sort, + }, context_instance=RequestContext(request)) + +@can_submit_liaison_required def liaison_approval_list(request): - if is_secretariat(request.user): - to_approve = LiaisonDetail.objects.filter(approved=None).order_by("-submitted") - else: - person = get_person_for_user(request.user) - approval_codes = IETFHM.get_all_can_approve_codes(person) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - to_approve = approvable_liaison_statements(approval_codes).order_by("-submitted") - else: - to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date") + liaisons = approvable_liaison_statements(request.user).order_by("-submitted") - return object_list(request, to_approve, - allow_empty=True, - template_name='liaisons/liaisondetail_approval_list.html', - ) + return render_to_response('liaisons/approval_list.html', { + "liaisons": liaisons, + }, context_instance=RequestContext(request)) -@can_submit_liaison +@can_submit_liaison_required def liaison_approval_detail(request, object_id): - if is_secretariat(request.user): - to_approve = LiaisonDetail.objects.filter(approved=None).order_by("-submitted") - else: - person = get_person_for_user(request.user) - approval_codes = IETFHM.get_all_can_approve_codes(person) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - to_approve = approvable_liaison_statements(approval_codes).order_by("-submitted") - else: - to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date") + liaison = get_object_or_404(approvable_liaison_statements(request.user), pk=object_id) if request.method=='POST' and request.POST.get('do_approval', False): - try: - liaison = to_approve.get(pk=object_id) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - liaison.approved = datetime.datetime.now() - liaison.save() - else: - approval = liaison.approval - if not approval: - approval = OutgoingLiaisonApproval.objects.create(approved=True, approval_date=datetime.datetime.now()) - liaison.approval = approval - liaison.save() - else: - approval.approved=True - approval.save() - liaison.send_by_email() - except LiaisonDetail.DoesNotExist: - pass - return HttpResponseRedirect(reverse('liaison_list')) - return object_detail(request, - to_approve, - object_id=object_id, - template_name='liaisons/liaisondetail_approval_detail.html', - ) + liaison.approved = datetime.datetime.now() + liaison.save() + + send_liaison_by_email(request, liaison) + return redirect('liaison_list') + + return render_to_response('liaisons/approval_detail.html', { + "liaison": liaison, + }, context_instance=RequestContext(request)) def _can_take_care(liaison, user): - if not liaison.deadline_date or liaison.action_taken: + if not liaison.deadline or liaison.action_taken: return False if user.is_authenticated(): @@ -224,15 +162,18 @@ def _can_take_care(liaison, user): def _find_person_in_emails(liaison, person): if not person: return False - emails = ','.join([ e for e in [liaison.cc1, liaison.cc2, liaison.to_email, - liaison.to_poc, liaison.submitter_email, - liaison.replyto, liaison.response_contact, - liaison.technical_contact] if e ]) + + emails = ','.join(e for e in [liaison.cc, liaison.to_contact, liaison.to_name, + liaison.reply_to, liaison.response_contact, + liaison.technical_contact] if e) for email in emails.split(','): name, addr = parseaddr(email) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - person.emailaddress_set = person.email_set - if email_re.search(addr) and person.emailaddress_set.filter(address=addr): + try: + validate_email(addr) + except ValidationError: + continue + + if person.email_set.filter(address=addr): return True elif addr in ('chair@ietf.org', 'iesg@ietf.org') and is_ietfchair(person): return True @@ -240,67 +181,31 @@ def _find_person_in_emails(liaison, person): return True elif addr in ('execd@iab.org', ) and is_iab_executive_director(person): return True + return False def liaison_detail(request, object_id): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - qfilter = Q() - public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by("-submitted_date") - else: - qfilter = Q(approval__isnull=True)|Q(approval__approved=True) - public_liaisons = LiaisonDetail.objects.filter(qfilter).order_by("-submitted_date") - liaison = get_object_or_404(public_liaisons, pk=object_id) - can_edit = False - user = request.user - can_take_care = _can_take_care(liaison, user) - if user.is_authenticated() and can_edit_liaison(user, liaison): - can_edit = True + liaison = get_object_or_404(LiaisonStatement.objects.exclude(approved=None), pk=object_id) + can_edit = request.user.is_authenticated() and can_edit_liaison(request.user, liaison) + can_take_care = _can_take_care(liaison, request.user) + if request.method == 'POST' and request.POST.get('do_action_taken', None) and can_take_care: liaison.action_taken = True liaison.save() can_take_care = False - relations = liaison.liaisondetail_set.filter(qfilter) - return object_detail(request, - public_liaisons, - template_name="liaisons/liaisondetail_detail.html", - object_id=object_id, - extra_context = {'can_edit': can_edit, - 'relations': relations, - 'can_take_care': can_take_care} - ) + + relations = liaison.liaisonstatement_set.all() + + return render_to_response("liaisons/detail.html", { + "liaison": liaison, + "can_edit": can_edit, + "can_take_care": can_take_care, + "relations": relations, + }, context_instance=RequestContext(request)) def liaison_edit(request, object_id): - liaison = get_object_or_404(LiaisonDetail, pk=object_id) - user = request.user - if not (user.is_authenticated() and can_edit_liaison(user, liaison)): - return HttpResponseForbidden('You have no permission to edit this liaison') + liaison = get_object_or_404(LiaisonStatement, pk=object_id) + if not (request.user.is_authenticated() and can_edit_liaison(request.user, liaison)): + return HttpResponseForbidden('You do not have permission to edit this liaison statement') return add_liaison(request, liaison=liaison) - -def ajax_liaison_list(request): - order = request.GET.get('order_by', 'submitted_date') - plain_order = order - reverse_order = order.startswith('-') - if reverse_order: - plain_order = order[1:] - if plain_order not in ('submitted_date', 'deadline_date', 'title', 'to_body', 'from_raw_body'): - order = 'submitted_date' - reverse_order = True - plain_order = 'submitted_date' - elif plain_order in ('submitted_date', 'deadline_date'): - # Reverse order for date fields, humans find it more natural - if reverse_order: - order = plain_order - else: - order = '-%s' % plain_order - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by(order) - else: - public_liaisons = LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by(order) - - return object_list(request, public_liaisons, - allow_empty=True, - template_name='liaisons/liaisondetail_simple_list.html', - extra_context={plain_order: not reverse_order and '-' or None} - ) diff --git a/ietf/liaisons/widgets.py b/ietf/liaisons/widgets.py index db5366eef..cd872a5e9 100644 --- a/ietf/liaisons/widgets.py +++ b/ietf/liaisons/widgets.py @@ -1,8 +1,11 @@ from django.conf import settings -from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse as urlreverse from django.db.models.query import QuerySet from django.forms.widgets import Select, Widget, TextInput from django.utils.safestring import mark_safe +from django.utils.html import conditional_escape + +from ietf.liaisons.models import LiaisonStatement class FromWidget(Select): @@ -14,9 +17,9 @@ class FromWidget(Select): def render(self, name, value, attrs=None, choices=()): all_choices = list(self.choices) + list(choices) - if len(all_choices)!=1 or \ - (isinstance(all_choices[0][1], (list, tuple)) and \ - len(all_choices[0][1])!=1): + if (len(all_choices) != 1 or + (isinstance(all_choices[0][1], (list, tuple)) and + len(all_choices[0][1]) != 1)): base = super(FromWidget, self).render(name, value, attrs, choices) else: option = all_choices[0] @@ -24,14 +27,14 @@ class FromWidget(Select): option = option[1][0] value = option[0] text = option[1] - base = u'%s' % (value, name, name, text) - base += u' (' + self.submitter + u')' + base = u'%s' % (conditional_escape(value), conditional_escape(name), conditional_escape(name), conditional_escape(text)) + base += u' (' + conditional_escape(self.submitter) + u')' if self.full_power_on: base += '' return mark_safe(base) @@ -39,7 +42,7 @@ class FromWidget(Select): class ReadOnlyWidget(Widget): def render(self, name, value, attrs=None): - html = u'
      %s
      ' % (name, value or '') + html = u'
      %s
      ' % (conditional_escape(name), conditional_escape(value or '')) return mark_safe(html) @@ -53,14 +56,14 @@ class ButtonWidget(Widget): super(ButtonWidget, self).__init__(*args, **kwargs) def render(self, name, value, attrs=None): - html = u'' % self.show_on - html += u'' % self.label + html = u'' % conditional_escape(self.show_on) + html += u'' % conditional_escape(self.label) if self.require: for i in self.require: - html += u'' % i - required_str = u'Please fill %s to attach a new file' % self.required_label - html += u'' % required_str - html += u'' % self.label + html += u'' % conditional_escape(i) + required_str = u'Please fill in %s to attach a new file' % conditional_escape(self.required_label) + html += u'' % conditional_escape(required_str) + html += u'' % conditional_escape(self.label) return mark_safe(html) @@ -71,8 +74,8 @@ class ShowAttachmentsWidget(Widget): html += u'' html += u'
      ' if value and isinstance(value, QuerySet): - for attach in value: - html += u'%s
      ' % (settings.LIAISON_ATTACH_URL, attach.file_id, attach.file_extension, attach.file_title, ) + for attachment in value: + html += u'%s
      ' % (settings.LIAISON_ATTACH_URL, conditional_escape(attachment.external_url), conditional_escape(attachment.title)) else: html += u'No files attached' html += u'
      ' @@ -88,23 +91,21 @@ class RelatedLiaisonWidget(TextInput): noliaison = 'inline' deselect = 'none' else: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail - liaison = LiaisonDetail.objects.get(pk=value) + liaison = LiaisonStatement.objects.get(pk=value) title = liaison.title if not title: - files = liaison.uploads_set.all() - if files: - title = files[0].file_title + attachments = liaison.attachments.all() + if attachments: + title = attachments[0].title else: title = 'Liaison #%s' % liaison.pk noliaison = 'none' deselect = 'inline' - html = u'No liaison selected' % noliaison - html += u'%s' % title - html += u' ' % (name, value) - html += u' ' % reverse('ajax_liaison_list') + html = u'No liaison selected' % conditional_escape(noliaison) + html += u'%s' % conditional_escape(title) + html += u' ' % (conditional_escape(name), conditional_escape(value)) + html += u' ' % urlreverse('ajax_liaison_list') html += u' ' - html += ' ' % name - html += '' % (deselect, name) + html += ' ' % conditional_escape(name) + html += '' % (conditional_escape(deselect), conditional_escape(name)) return mark_safe(html) diff --git a/ietf/templates/liaisons/approval_detail.html b/ietf/templates/liaisons/approval_detail.html new file mode 100644 index 000000000..6b94c32e2 --- /dev/null +++ b/ietf/templates/liaisons/approval_detail.html @@ -0,0 +1,10 @@ +{% extends "liaisons/detail.html" %} +{# Copyright The IETF Trust 2007, All Rights Reserved #} + +{% block content %} +{{ block.super }} + +
      + +
      +{% endblock %} diff --git a/ietf/templates/liaisons/liaisondetail_approval_list.html b/ietf/templates/liaisons/approval_list.html similarity index 86% rename from ietf/templates/liaisons/liaisondetail_approval_list.html rename to ietf/templates/liaisons/approval_list.html index aba7092d4..0e5dba6d7 100644 --- a/ietf/templates/liaisons/liaisondetail_approval_list.html +++ b/ietf/templates/liaisons/approval_list.html @@ -1,4 +1,4 @@ -{% extends "liaisons/liaisondetail_list.html" %} +{% extends "liaisons/overview.html" %} {# Copyright The IETF Trust 2007, All Rights Reserved #} {% block title %}Pending Liaison Statements{% endblock %} diff --git a/ietf/templates/liaisons/detail.html b/ietf/templates/liaisons/detail.html new file mode 100644 index 000000000..a864da1cf --- /dev/null +++ b/ietf/templates/liaisons/detail.html @@ -0,0 +1,120 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2007, All Rights Reserved #} +{% load ietf_filters %} + +{% block title %}Liaison Statement: {% include 'liaisons/liaison_title.html' %}{% endblock %} + +{% block pagehead %} + + + +{% endblock %} + +{% block morecss %} +.ietf-liaison-details tr { vertical-align:top; } +{% endblock morecss %} + +{% block content %} +

      Liaison Statement: {% include 'liaisons/liaison_title.html' %}

      + + + + + + + + + + + + + + + +{% if liaison.from_contact %} + + + + + + + + + + + + + + + + + + + + {% if liaison.deadline %} + + + + + {% endif %} +{% endif %} + +{% if relations %} + + + + +{% endif %} + +{% if liaison.related_to %} + + + + +{% endif %} + + + + + + +{% if liaison.from_contact and liaison.body %} + + + + +{% endif %} +
      Submission Date:{{ liaison.submitted|date:"Y-m-d" }}
      From:{{ liaison.from_name }} + {% if liaison.from_contact %}({{ liaison.from_contact.person }}){% endif %} +
      To: + {% if liaison.from_contact %} + {{ liaison.to_name }} ({{ liaison.to_contact|parse_email_list }}) + {% else %} + {{ liaison.to_name|urlize }} + {% endif %} +
      Cc:{{ liaison.cc|parse_email_list|make_one_per_line|safe|linebreaksbr }}
      Response Contact:{{ liaison.response_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}
      Technical Contact:{{ liaison.technical_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}
      Purpose:{{ liaison.purpose.name }}
      Deadline:{{ liaison.deadline }} + {% if liaison.action_taken %}Action Taken{% else %}Action Needed{% endif %} + {% if can_take_care %} +
      + +
      + {% endif %} +
      Liaisons referring to this one: + {% for rel in relations %} + {% if rel.title %}{{ rel.title }}{% else %}Liaison #{{ rel.pk }}{% endif %}
      + {% endfor %} +
      Referenced liaison: + {% if liaison.related_to.title %}{{ liaison.related_to.title }}{% else %}Liaison #{{ liaison.related_to.pk }}{% endif %} +
      Attachments: + {% for doc in liaison.attachments.all %} + {{ doc.title }}{% if not forloop.last %}
      {% endif %} + {% empty %} + (none) + {% endfor %} +
      Body:
      {{ liaison.body|wordwrap:"71" }}
      + +{% if can_edit %} +

      Edit Liaison

      +{% endif %} + +{% endblock %} diff --git a/ietf/templates/liaisons/edit.html b/ietf/templates/liaisons/edit.html new file mode 100644 index 000000000..8286ebf34 --- /dev/null +++ b/ietf/templates/liaisons/edit.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2007, All Rights Reserved #} +{% load ietf_filters %} +{% block title %}{% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}{% endblock %} + +{% block pagehead %} + + +{% endblock %} + +{% block content %} +

      {% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}

      + + + +{% if not liaison %} +
        +
      • If you wish to submit your liaison statement by e-mail, then please send it to statements@ietf.org
      • +
      • Fields marked with * are required. For detailed descriptions of the fields see Field help
      • +
      +{% endif %} + +{{ form }} + +{% endblock %} + +{% block js %} + + +{% endblock %} diff --git a/ietf/templates/liaisons/liaison_mail.txt b/ietf/templates/liaisons/liaison_mail.txt index a79a1a437..c9c2369da 100644 --- a/ietf/templates/liaisons/liaison_mail.txt +++ b/ietf/templates/liaisons/liaison_mail.txt @@ -1,19 +1,19 @@ {% load ietf_filters %}{% autoescape off %}Title: {{ liaison.title|clean_whitespace }} -Submission Date: {{ liaison.submitted_date }} +Submission Date: {{ liaison.submitted|date:"Y-m-d" }} URL of the IETF Web page: {{ url }} -{% if liaison.deadline_date %}Please reply by {{ liaison.deadline_date }}{% endif %} -From: {{ liaison.from_body }} ({{ liaison.person }} <{{ liaison.replyto|default:liaison.from_email }}>) -To: {{ liaison.to_body }} ({{ liaison.to_poc }}) -Cc: {{ liaison.cc1 }} -Reponse Contact: {{ liaison.response_contact }} +{% if liaison.deadline %}Please reply by {{ liaison.deadline }}{% endif %} +From: {{ liaison.from_name }} ({{ liaison.from_contact.person }} <{{ liaison.reply_to|default:liaison.from_contact.address }}>) +To: {{ liaison.to_name }} ({{ liaison.to_contact }}) +Cc: {{ liaison.cc }} +Response Contact: {{ liaison.response_contact }} Technical Contact: {{ liaison.technical_contact }} -Purpose: {% if liaison.purpose_text %}{{ liaison.purpose_text }}{% else %}{{ liaison.purpose }}{% endif %} +Purpose: {{ liaison.purpose.name }} {% if liaison.related_to %}Referenced liaison: {% if liaison.related_to.title %}{{ liaison.related_to.title }}{% else %}Liaison #{{ liaison.related_to.pk }}{% endif %} ({{ referenced_url }}){% endif %} Body: {{ liaison.body }} Attachments: -{% for file in liaison.uploads_set.all %} - {{ file.file_title }} - https://datatracker.ietf.org/documents/LIAISON/{{ file.filename }} +{% for doc in liaison.attachments.all %} + {{ doc.title }} + https://datatracker.ietf.org/documents/LIAISON/{{ doc.external_url }} {% empty %} No document has been attached {% endfor %}{% endautoescape %} diff --git a/ietf/templates/liaisons/liaison_mail_detail.html b/ietf/templates/liaisons/liaison_mail_detail.html deleted file mode 100644 index 43ad036ae..000000000 --- a/ietf/templates/liaisons/liaison_mail_detail.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% load ietf_filters %} -{% block title %}{{ liaison.title }}{% endblock %} - -{% block content %} -

      Simulated Mail for Liaison Statement

      - -

      -Demo version of this tool does NOT actually send the liaison statement to the recipients. -

      -

      -Rather, the actual email body (including mail header) is displayed below. -

      -

      -In production mode, you will not see this screen. -

      - -
      -From: {{ message.From }}
      -To: {{ message.To }}
      -Cc: {{ message.Cc }}
      -Bcc: {{ message.Bcc }}
      -Subject: {{ message.Subject }}
      -
      -{{ mail.body }}
      -
      - -Return to liaison list - -{% endblock %} diff --git a/ietf/templates/liaisons/liaison_main_management.html b/ietf/templates/liaisons/liaison_main_management.html deleted file mode 100644 index edc259a6f..000000000 --- a/ietf/templates/liaisons/liaison_main_management.html +++ /dev/null @@ -1,62 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% load ietf_filters %} -{% block title %}Liaison Management{% endblock %} - -{% block content %} -

      Liaison Management

      - -{% if can_send_incoming or can_send_outgoing %} -
      -

      Add new liaison

      -
      -{% endif %} - -{% if to_aprove %} -
      -

      Liaisons that need your approval

      - {% for liaison in to_aprove %} - {{ liaison }} - {% endfor %} -
      -{% endif %} - -{% if to_edit %} -
      -

      Liaisons you can edit

      -{% for liaison in to_edit %} - -{{ liaison.submitted_date|date:"Y-m-d" }} -{{ liaison.from_body|escape }} - -{% if liaison.by_secretariat %} - {% if liaison.submitter_email %} - {{ liaison.submitter_name|escape }} - {% else %} - {{ liaison.submitter_name|escape }} - {% endif %} -{% else %} - {{ liaison.to_body|escape }} -{% endif %} - - -{% if liaison.by_secretariat %} - {% for file in liaison.uploads_set.all %} - {{ file.file_title|escape }}
      - {% endfor %} -{% else %} - {{ liaison.title|escape }} -{% endif %} - - -{% endfor %} - - - -
      -{% endif %} - -{% endblock %} diff --git a/ietf/templates/liaisons/liaison_table.html b/ietf/templates/liaisons/liaison_table.html new file mode 100644 index 000000000..60fc22f9a --- /dev/null +++ b/ietf/templates/liaisons/liaison_table.html @@ -0,0 +1,39 @@ +{% load ietf_filters %} + + + + + + + + + + +{% for liaison in liaisons %} + + + + + + + +{% endfor %} + +
      DateFromToDeadlineTitle
      {{ liaison.submitted|date:"Y-m-d" }}{{ liaison.from_name }} + {% if liaison.from_contact_id %} + {{ liaison.to_name }} + {% else %} + {{ liaison.to_name|strip_email }} + {% endif %} + + {{ liaison.deadline|default:"-"|date:"Y-m-d" }} + + {% if not liaison.from_contact_id %} + {% for doc in liaison.attachments.all %} + {{ doc.title }}
      + {% endfor %} + {% else %} + {{ liaison.title }} + {% endif %} + +
      diff --git a/ietf/templates/liaisons/liaison_title.html b/ietf/templates/liaisons/liaison_title.html index dbf83f37c..5556aaafa 100644 --- a/ietf/templates/liaisons/liaison_title.html +++ b/ietf/templates/liaisons/liaison_title.html @@ -1,5 +1,5 @@ -{% if object.by_secretariat %} -Liaison statement submitted by email from {{ object.from_body|escape }} to {{ object.submitter_name|escape }} on {{ object.submitted_date }} +{% load ietf_filters %}{% if not liaison.from_contact %} + Liaison statement submitted by email from {{ liaison.from_name }} to {{ liaison.to_name|strip_email }} on {{ liaison.submitted|date:"Y-m-d" }} {% else %} -{{ object.title }} + {{ liaison.title }} {% endif %} diff --git a/ietf/templates/liaisons/liaisondetail_approval_detail.html b/ietf/templates/liaisons/liaisondetail_approval_detail.html deleted file mode 100644 index 2391dc956..000000000 --- a/ietf/templates/liaisons/liaisondetail_approval_detail.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "liaisons/liaisondetail_detail.html" %} -{# Copyright The IETF Trust 2007, All Rights Reserved #} - -{% block content %} -{{ block.super }} - -
      - -
      -{% endblock %} diff --git a/ietf/templates/liaisons/liaisondetail_detail.html b/ietf/templates/liaisons/liaisondetail_detail.html deleted file mode 100644 index cc151b62e..000000000 --- a/ietf/templates/liaisons/liaisondetail_detail.html +++ /dev/null @@ -1,110 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% load ietf_filters %} -{% block title %}Liaison Statement: {% include 'liaisons/liaison_title.html' %}{% endblock %} - -{% block pagehead %} - - - -{% endblock %} - -{% block morecss %} -.ietf-liaison-details tr { vertical-align:top; } -{% endblock morecss %} - -{% block content %} -

      Liaison Statement: {% include 'liaisons/liaison_title.html' %}

      - - - - - - - - -{% endif %} - -{% if not object.by_secretariat %} - - - - - - - - - - - - -{% if object.deadline_date %} - - -{% if can_take_care %} - -{% else %} - -{% endif %} - -{% endif %} - -{% endif %} - -{% if relations %} - - - -{% endif %} - -{% if object.related_to %} - - - -{% endif %} - - - - - -{% if not object.by_secretariat and object.body %} - - -{% endif %} -
      Submission Date:{{ object.submitted_date }}
      From:{{ object.from_body }} ({{ object.person }})
      To: -{% if object.by_secretariat %} - {% if object.submitter_email %} - {{ object.submitter_name }} - {% else %} - {{ object.submitter_name }} - {% endif %} -{% else %} -{{ object.to_body }} ({{ object.to_poc|parse_email_list|safe }})
      Cc:{{ object.cc1|parse_email_list|make_one_per_line|safe|linebreaksbr }}
      Response Contact: -{{ object.response_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }} -
      Technical Contact:{{ object.technical_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}
      Purpose:{% if object.purpose_text %}{{ object.purpose_text }}{% else %}{{ object.purpose }}{% endif %}
      Deadline:
      -{{ object.deadline_date }} -{% if object.action_taken %}Action Taken{% else %}Action Required{% endif %} - -
      {{ object.deadline_date }} -{% if object.action_taken %}Action Taken{% else %}Action Needed{% endif %} -
      Liaisons referring to this one: -{% for liaison in relations %} -{% if liaison.title %}{{ object.title }}{% else %}Liaison #{{ liaison.pk }}{% endif %}
      -{% endfor %} -
      Referenced liaison: -{% if object.related_to.title %}{{ object.related_to.title }}{% else %}Liaison #{{ object.related_to.pk }}{% endif %} -
      Attachments: -{% for file in object.uploads_set.all %} -{{ file.file_title }}{% if not forloop.last %}
      {% endif %} -{% empty %} -(none) -{% endfor %} -
      Body:
      {{ object.body|wordwrap:"71"|escape }}
      - -{% if can_edit %} -
      - -
      -{% endif %} - -{% endblock %} diff --git a/ietf/templates/liaisons/liaisondetail_edit.html b/ietf/templates/liaisons/liaisondetail_edit.html deleted file mode 100644 index 59757909b..000000000 --- a/ietf/templates/liaisons/liaisondetail_edit.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2007, All Rights Reserved #} -{% load ietf_filters %} -{% block title %}{% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}{% endblock %} - -{% block pagehead %} -{{ form.media }} -{% endblock %} - -{% block content %} -

      {% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}

      - -
      -Your browser has Javascript disabled. Please enable javascript and reload the page. - -
      - -{% if not liaison %} -
        -
      • If you wish to submit your liaison statement by e-mail, then please send it to statements@ietf.org
      • -
      • Fields marked with * are required. For detailed descriptions of the fields see Field help
      • -
      -{% endif %} - -{{ form }} - -{% endblock %} diff --git a/ietf/templates/liaisons/liaisondetail_simple_list.html b/ietf/templates/liaisons/liaisondetail_simple_list.html deleted file mode 100644 index f93088f2a..000000000 --- a/ietf/templates/liaisons/liaisondetail_simple_list.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - -{% for liaison in object_list %} - - - - - - - -{% endfor %} - -
      - From - - To - - Deadline - - Title -
      {{ liaison.submitted_date|date:"Y-m-d" }}{{ liaison.from_body|escape }} -{% if liaison.by_secretariat %} - {% if liaison.submitter_email %} - {{ liaison.submitter_name|escape }} - {% else %} - {{ liaison.submitter_name|escape }} - {% endif %} -{% else %} - {{ liaison.to_body|escape }} -{% endif %} - - {{ liaison.deadline_date|default:"--" }} - -{% if liaison.by_secretariat %} - {% for file in liaison.uploads_set.all %} - {{ file.file_title|escape }}
      - {% endfor %} -{% else %} - {{ liaison.title|escape }} -{% endif %} - -
      diff --git a/ietf/templates/liaisons/liaisondetail_list.html b/ietf/templates/liaisons/overview.html similarity index 72% rename from ietf/templates/liaisons/liaisondetail_list.html rename to ietf/templates/liaisons/overview.html index 9eaad8682..16c1d510c 100644 --- a/ietf/templates/liaisons/liaisondetail_list.html +++ b/ietf/templates/liaisons/overview.html @@ -18,12 +18,12 @@ {% endif %} {% endblock %} -{% include "liaisons/liaisondetail_simple_list.html" %} +{% include "liaisons/liaison_table.html" %} {% endblock %} diff --git a/static/css/liaisons.css b/static/css/liaisons.css index c68075d6e..522620357 100644 --- a/static/css/liaisons.css +++ b/static/css/liaisons.css @@ -96,30 +96,17 @@ span.fieldRequired { background-color: #ffdd88; } -th.orderField a { +th.sort a { text-decoration: none; color: white; - display: block; + padding-right: 20px; + background: url(/images/sort-header-clear.png) no-repeat right center; } -th.orderFieldReversed a { - background: #2647A0 url(/images/arrow-down.gif) no-repeat left center; - padding-left: 20px; +th.sorted a { + background: url(/images/sort-header-filled.png) no-repeat right center; } -th.orderFieldActive a { - background: #2647A0 url(/images/arrow-up.gif) no-repeat left center; - padding-left: 20px; -} - -.noActionTaken, -.actionTaken { - border: 1px solid green; - padding: 2px 5px; - background-color: #ccffbb; -} - -.noActionTaken { - border: 1px solid red; - background-color: #ffccbb; -} +.noActionTaken, .actionTaken { padding: 2px 5px; } +.actionTaken { border: 1px solid green; background-color: #ccffbb; } +.noActionTaken { border: 1px solid red; background-color: #ffccbb; } From 5f002cf0ac7dba8d0ba11d1c369b612e23aa1f66 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 5 Dec 2013 15:30:17 +0000 Subject: [PATCH 139/173] Fix some test errors - Legacy-Id: 6795 --- ietf/doc/tests_draft.py | 2 +- ietf/liaisons/tests.py | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index f11383ec9..dc35b1a32 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -446,7 +446,7 @@ class ExpireIDsTests(TestCase): Meeting.objects.create(number="123", type=MeetingTypeName.objects.get(slug="ietf"), - date=date.today()) + date=datetime.date.today()) second_cut_off = Meeting.get_second_cut_off() ietf_monday = Meeting.get_ietf_monday() diff --git a/ietf/liaisons/tests.py b/ietf/liaisons/tests.py index e358b3176..e938ca373 100644 --- a/ietf/liaisons/tests.py +++ b/ietf/liaisons/tests.py @@ -6,27 +6,15 @@ from django.core.urlresolvers import reverse as urlreverse from StringIO import StringIO from pyquery import PyQuery -from ietf.utils.test_utils import SimpleUrlTestCase, canonicalize_feed, canonicalize_sitemap, login_testing_unauthorized +from ietf.utils.test_utils import TestCase, login_testing_unauthorized from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox -from ietf.utils import TestCase from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName from ietf.person.models import Person, Email from ietf.group.models import Group, Role from ietf.liaisons.mails import send_sdo_reminder, possibly_send_deadline_reminder -class LiaisonsUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) - def doCanonicalize(self, url, content): - if url.startswith("/feed/"): - return canonicalize_feed(content) - elif url == "/sitemap-liaison.xml": - return canonicalize_sitemap(content) - else: - return content - def make_liaison_models(): sdo = Group.objects.create( name="United League of Marsmen", From fe5ad0c998a249457294cac0732b1d45e8cda368 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Thu, 5 Dec 2013 15:52:35 +0000 Subject: [PATCH 140/173] Remove unused i18n import in liaisonform.html - Legacy-Id: 6796 --- ietf/templates/liaisons/liaisonform.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/ietf/templates/liaisons/liaisonform.html b/ietf/templates/liaisons/liaisonform.html index 980589ca5..7e5961051 100644 --- a/ietf/templates/liaisons/liaisonform.html +++ b/ietf/templates/liaisons/liaisonform.html @@ -1,5 +1,3 @@ -{% load i18n %} -