diff --git a/ietf/idtracker/templatetags/ietf_filters.py b/ietf/idtracker/templatetags/ietf_filters.py index 321bc7d6a..ce216e8d7 100644 --- a/ietf/idtracker/templatetags/ietf_filters.py +++ b/ietf/idtracker/templatetags/ietf_filters.py @@ -431,6 +431,15 @@ def stable_dictsort(value, arg): decorated.sort(lambda a, b: cmp(a[0], b[0]) if a[0] and b[0] else -1 if b[0] else 1 if a[0] else 0) return [item[1] for item in decorated] +@register.filter +def ad_area(user): + if user and user.is_authenticated(): + from redesign.group.models import Group + g = Group.objects.filter(role__name="ad", role__person__user=user) + if g: + return g[0].acronym + return None + def _test(): import doctest doctest.testmod() diff --git a/ietf/ietfworkflows/forms.py b/ietf/ietfworkflows/forms.py index b6885729c..b63efc57f 100644 --- a/ietf/ietfworkflows/forms.py +++ b/ietf/ietfworkflows/forms.py @@ -22,6 +22,7 @@ from redesign.doc.utils import get_tags_for_stream_id from redesign.doc.models import save_document_in_history, DocEvent, Document from redesign.name.models import DocTagName, StreamName, RoleName from redesign.group.models import Group, GroupStateTransitions, Role +from redesign.group.utils import save_group_in_history from redesign.person.models import Person, Email class StreamDraftForm(forms.Form): @@ -351,9 +352,10 @@ class StreamDelegatesForm(forms.Form): def save(self): if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # FIXME: should save group history here + stream_group = Group.objects.get(acronym=self.stream.slug) + save_group_in_history(stream_group) Role.objects.get_or_create(person=self.person, - group=Group.objects.get(acronym=self.stream.slug), + group=stream_group, name=RoleName.objects.get(slug="delegate"), email=Email.objects.get(address=self.cleaned_data.get('email'))) return diff --git a/ietf/ietfworkflows/views.py b/ietf/ietfworkflows/views.py index f24bd4d4e..49607fb31 100644 --- a/ietf/ietfworkflows/views.py +++ b/ietf/ietfworkflows/views.py @@ -19,6 +19,8 @@ from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, is_chair_of_stream, can_adopt) from redesign.doc.utils import get_tags_for_stream_id from redesign.name.models import DocTagName +from redesign.group.utils import save_group_in_history +from redesign.group.models import Group, Role REDUCED_HISTORY_LEN = 20 @@ -125,9 +127,9 @@ def stream_delegates(request, stream_name): if request.POST.get('delete', False): pk_list = request.POST.getlist('remove_delegate') if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # FIXME: should save group history here - from redesign.group.models import Role - Role.objects.filter(person__in=pk_list, group__acronym=stream.slug, name="delegate").delete() + 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: diff --git a/ietf/settings.py b/ietf/settings.py index 0e41fe8ce..e0c04dec3 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -156,6 +156,7 @@ INSTALLED_APPS = ( 'ietf.submit', 'ietf.ietfworkflows', 'ietf.wgchairs', + 'ietf.wgcharter', ) INTERNAL_IPS = ( @@ -187,6 +188,8 @@ MAX_WG_DELEGATES = 3 INTERNET_DRAFT_PATH = '/a/www/ietf-ftp/internet-drafts/' INTERNET_DRAFT_PDF_PATH = '/a/www/ietf-datatracker/pdf/' RFC_PATH = '/a/www/ietf-ftp/rfc/' +CHARTER_PATH = '/a/www/ietf-ftp/charters/' +CHARTER_TXT_URL = 'http://www.ietf.org/charters/' AGENDA_PATH = '/a/www/www6s/proceedings/' AGENDA_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/agenda/%(wg)s.%(ext)s' MINUTES_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/minutes/%(wg)s.%(ext)s' diff --git a/ietf/templates/base_leftmenu.html b/ietf/templates/base_leftmenu.html index 9eed4ec61..5d4c48db8 100644 --- a/ietf/templates/base_leftmenu.html +++ b/ietf/templates/base_leftmenu.html @@ -42,12 +42,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  • My Documents
  • Next Telechat
  • Discusses
  • -
  • Working Groups
  • + {# FIXME: wgcharter
  • Working Groups
  • #} {% endif %} {% if user|in_group:"Secretariat" %}
  • Secretariat
  • Telechat Dates
  • -
  • Working Groups
  • + {# FIXME: wgcharter
  • Working Groups
  • #} {% endif %} {% if user %} {% get_user_managed_streams user as stream_list %} @@ -67,6 +67,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  • Active WGs
  • + {# FIXME: wgcharter
  • Proposed WGs
  • #}
  • Concluded WGs
  • Non-WG Lists
  • @@ -83,6 +84,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  • Upcoming
  • Other Documents
  • + {# FIXME: wgcharter
  • WG Charters
  • #}
  • IPR Disclosures
  • Liaison Statements
  • IESG Agenda
  • diff --git a/ietf/templates/feeds/wg_charter_description.html b/ietf/templates/feeds/wg_charter_description.html new file mode 100644 index 000000000..a171e5675 --- /dev/null +++ b/ietf/templates/feeds/wg_charter_description.html @@ -0,0 +1,28 @@ +{# Copyright The IETF Trust 2011, All Rights Reserved #} +{% load ietf_filters %} +{{ obj.info.text|safe }}
    +
    +{% with obj.group as wg %} +WG name: {{ wg.name }}
    +WG acronym: {{ wg.acronym }}
    +IETF area: {{ wg.parent|default:"-" }}
    + +WG chairs: {% for n in obj.chairs %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}
    +WG secretaries: {% for n in obj.secr %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}
    +WG technical advisors: {% for n in obj.techadv %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}
    +Assigned AD: {{ wg.ad }}
    + +Mailing list: {{ wg.list_email }}
    +Mailing list subscribe {{ wg.list_subscribe }}
    +Mailing list archive: {{ wg.list_archive }}
    +Other web sites: {% for a in wg.groupurl_set.all %}{{ a.url }} {% if a.name %}({{ a.name }}){% endif %}{% if not forloop.last %}, {% endif %}{% endfor %}
    + +WG State: {{ wg.state|safe }}
    +Charter State: {{ wg.charter.charter_state|safe }}
    +
    +{% if obj.rev %} +{{ obj.charter|safe }} +{% else %} +The WG does not yet have a charter +{% endif %} +{% endwith %} diff --git a/ietf/templates/feeds/wg_charter_title.html b/ietf/templates/feeds/wg_charter_title.html new file mode 100644 index 000000000..29cd881f9 --- /dev/null +++ b/ietf/templates/feeds/wg_charter_title.html @@ -0,0 +1 @@ +{% load ietf_filters %}WG Charter for {{obj.group.name}} ({{ obj.group.acronym|safe }}) diff --git a/ietf/templates/wgcharter/action_text.txt b/ietf/templates/wgcharter/action_text.txt new file mode 100644 index 000000000..0e9a18a0c --- /dev/null +++ b/ietf/templates/wgcharter/action_text.txt @@ -0,0 +1,10 @@ +{% load ietf_filters %}{% autoescape off %}From: The IESG +To: IETF-Announce +Subject: WG Action: {{ action_type }} {{ wg.name }} ({{wg.acronym}}) + +{% filter wordwrap:73 %}{% ifequal action_type "Formed" %}A new IETF working group has been formed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal action_type "Rechartered" %}The {{ wg.name }} ({{wg.acronym}}) working group in the {{ wg.parent.name }} of the IETF has been rechartered.{% endifequal %} For additional information please contact the Area Directors or the WG Chair. + +{% include "wgcharter/wg_info.txt" %} + +{% endfilter %} +{% endautoescape %} diff --git a/ietf/templates/wgcharter/add_comment.html b/ietf/templates/wgcharter/add_comment.html new file mode 100644 index 000000000..2dc46ebb2 --- /dev/null +++ b/ietf/templates/wgcharter/add_comment.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% block title %}Add comment on {{ wg.acronym }}{% endblock %} + +{% block morecss %} +form.add-comment #id_comment { + width: 600px; + height: 300px; +} + +form.add-comment .actions { + padding-top: 20px; +} +{% endblock %} + +{% block content %} +

    Add comment on {{ wg.acronym }}

    + +

    The comment will be added to the history trail.

    + +
    + + {{ form.as_table }} + + + + +
    + Back + +
    +
    +{% endblock %} diff --git a/ietf/templates/wgcharter/announcement_text.html b/ietf/templates/wgcharter/announcement_text.html new file mode 100644 index 000000000..dda92b9f3 --- /dev/null +++ b/ietf/templates/wgcharter/announcement_text.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} + +{% block title %}WG {{ announcement }} announcement writeup for {{ charter.chartered_group.acronym }}{% endblock %} + +{% block morecss %} +form #id_announcement_text { + width: 700px; + height: 600px; +} +{% endblock %} + +{% block content %} +

    WG {{ announcement }} announcement writeup for {{ charter.chartered_group.acronym }}

    + +
    + + {{ announcement_text_form.announcement_text }} + +
    + Back + + +
    + +{% load ietf_filters %} +{% if user|in_group:"Secretariat" %} +
    + +
    +{% endif %} + +
    + +{% endblock%} diff --git a/ietf/templates/wgcharter/approve_ballot.html b/ietf/templates/wgcharter/approve_ballot.html new file mode 100644 index 000000000..fe42afda2 --- /dev/null +++ b/ietf/templates/wgcharter/approve_ballot.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block title %}Approve ballot for {{ wg.acronym }}{% endblock %} + +{% block morecss %} +form.approve-ballot pre { + margin: 0; + padding: 4px; + border-top: 4px solid #eee; + border-bottom: 4px solid #eee; +} +form.approve-ballot .announcement { + overflow-x: auto; + overflow-y: scroll; + width: 800px; + height: 400px; + border: 1px solid #bbb; +} +{% endblock %} + +{% block content %} +

    Approve Ballot for {{ wg.acronym }}

    + +
    IETF announcement:
    + +
    + +
    +
    {{ announcement }}
    +
    + +
    + Back + +
    +
    +{% endblock %} diff --git a/ietf/templates/wgcharter/ballot_comment_mail.txt b/ietf/templates/wgcharter/ballot_comment_mail.txt new file mode 100644 index 000000000..748bf328f --- /dev/null +++ b/ietf/templates/wgcharter/ballot_comment_mail.txt @@ -0,0 +1,25 @@ +{% autoescape off %}{{ ad }} has entered the following ballot position for +{{ charter.name }}-{{ charter.rev }}: {{ pos.name }} + +When responding, please keep the subject line intact and reply to all +email addresses included in the To and CC lines. (Feel free to cut this +introductory paragraph, however.) + +{% if not block_comment and not comment %} +There is no BLOCK or COMMENT text associated with this position. +{% endif %} + +{% if block_comment %}---------------------------------------------------------------------- +BLOCK: +---------------------------------------------------------------------- + +{{ block_comment|safe|wordwrap:73 }} + + +{% endif %}{% if comment %}---------------------------------------------------------------------- +COMMENT: +---------------------------------------------------------------------- + +{{ comment|safe|wordwrap:73 }} +{% endif %} +{% endautoescape %} diff --git a/ietf/templates/wgcharter/ballot_issued.html b/ietf/templates/wgcharter/ballot_issued.html new file mode 100644 index 000000000..429961234 --- /dev/null +++ b/ietf/templates/wgcharter/ballot_issued.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block title %}Ballot for {{ charter.chartered_group }} issued{% endblock %} + +{% block content %} +

    Ballot for {{ charter.chartered_group }} issued

    + +

    Ballot has been sent out.

    + + +{% endblock %} diff --git a/ietf/templates/wgcharter/ballot_writeup.txt b/ietf/templates/wgcharter/ballot_writeup.txt new file mode 100644 index 000000000..552fe65e8 --- /dev/null +++ b/ietf/templates/wgcharter/ballot_writeup.txt @@ -0,0 +1,51 @@ + +Technical Summary + + Relevant content can frequently be found in the abstract + and/or introduction of the document. If not, this may be + an indication that there are deficiencies in the abstract + or introduction. + +Working Group Summary + + Was there anything in the WG process that is worth noting? + For example, was there controversy about particular points + or were there decisions where the consensus was + particularly rough? + +Document Quality + + Are there existing implementations of the protocol? Have a + significant number of vendors indicated their plan to + implement the specification? Are there any reviewers that + merit special mention as having done a thorough review, + e.g., one that resulted in important changes or a + conclusion that the document had no substantive issues? If + there was a MIB Doctor, Media Type, or other Expert Review, + what was its course (briefly)? In the case of a Media Type + Review, on what date was the request posted? + +Personnel + + Who is the Document Shepherd for this document? Who is the + Responsible Area Director? If the document requires IANA + experts(s), insert 'The IANA Expert(s) for the registries + in this document are .' + +RFC Editor Note + + (Insert RFC Editor Note here or remove section) + +IRTF Note + + (Insert IRTF Note here or remove section) + +IESG Note + + (Insert IESG Note here or remove section) + +IANA Note + + (Insert IANA Note here or remove section) + + diff --git a/ietf/templates/wgcharter/ballot_writeupnotes.html b/ietf/templates/wgcharter/ballot_writeupnotes.html new file mode 100644 index 000000000..be58ec788 --- /dev/null +++ b/ietf/templates/wgcharter/ballot_writeupnotes.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% block title %}Ballot writeup and notes for {{ charter.chartered_group }}{% endblock %} + +{% block morecss %} +form #id_ballot_writeup { + width: 700px; + height: 600px; +} +{% endblock %} + +{% block content %} +

    Ballot writeup and notes for {{ charter.chartered_group }}

    + + +
    + +

    (Working Group Summary, Personnel, IAB Note, IESG Note, IANA Note)

    + + {{ ballot_writeup_form.ballot_writeup }} + +
    + Back + + +
    +
    + + +{% endblock%} diff --git a/ietf/templates/wgcharter/by_area.html b/ietf/templates/wgcharter/by_area.html new file mode 100644 index 000000000..3f7d3c0ad --- /dev/null +++ b/ietf/templates/wgcharter/by_area.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% block title %}Working Groups for {{ area_name }}{% endblock %} + +{% block content %} +

    Working Groups for {{ area_name }}

    + +

    +Start new WG charter effort +

    + +{% if not recs %} +

    No WGs match your query.

    +{% else %} + + + {% for hdr in meta.hdrs %} + {% include "wgcharter/table_header.html" %} + {% endfor %} + +{% for wg in recs %} +{% include "wgcharter/search_result_row.html" %} +{% endfor %} +
    +{% endif %} +{% endblock content %} diff --git a/ietf/templates/wgcharter/change_state.html b/ietf/templates/wgcharter/change_state.html new file mode 100644 index 000000000..f6442d0b2 --- /dev/null +++ b/ietf/templates/wgcharter/change_state.html @@ -0,0 +1,91 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block morecss %} +form.change-state select { + width: 22em; +} + +#id_message, #id_comment { + width: 40em; +} + +form.change-state .actions { + text-align: right; + padding-top: 10px; +} + +{% endblock %} + +{% block content %} +

    {{ title }}

    + +{% if "Change state" in title %}

    For help on the states, see the state table.

    {% endif %} + +
    + + {% for field in form.visible_fields %} + + {% if field.name == "initial_time" %} + {% if option == "recharter" %} + + + + + + {% endfor %} + + + +
    {{ field.label_tag }}:{{ field }} + {% if field.help_text %}
    {{ field.help_text }}
    {% endif %} + {% else %} + {% if option == "initcharter" %} +
    {{ field.label_tag }}:{{ field }} + {% if field.help_text %}
    {{ field.help_text }}
    {% endif %} + {% endif %} + {% endif %} + {% else %} +
    {{ field.label_tag }}:{{ field }} + {% if field.help_text %}
    {{ field.help_text }}
    {% endif %} + {% endif %} + {% if field.name == "charter_state" and field.errors == "warning" %} +
    • The initial review time hasn't elapsed. Select this checkbox to proceed anyway:
    + {% else %} + {{ field.errors }} + {% endif %} +
    + {% if option %} + + {% else %} + Back + + {% endif %} +
    +
    + +{% if prev_state %} +

    Or revert to previous state

    + +
    +
    + + + +
    +
    +{% endif %} +{% endblock %} + +{% block content_end %} + + +{% endblock %} diff --git a/ietf/templates/wgcharter/charter_diffs.html b/ietf/templates/wgcharter/charter_diffs.html new file mode 100644 index 000000000..413ffd4b5 --- /dev/null +++ b/ietf/templates/wgcharter/charter_diffs.html @@ -0,0 +1,34 @@ +
    +

    Diffs

    +
    + + + + + + + +
    + + +Format: + +
    + + +
    +
    +
    diff --git a/ietf/templates/wgcharter/conclude.html b/ietf/templates/wgcharter/conclude.html new file mode 100644 index 000000000..664b07174 --- /dev/null +++ b/ietf/templates/wgcharter/conclude.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} + +{% block title %}Request closing of WG {{ wg.acronym }}{% endblock %} + +{% block morecss %} +#id_instructions { + width: 40em; +} + +form.conclude .actions { + text-align: right; + padding-top: 10px; +} +{% endblock %} + +{% block content %} +

    Request closing of {{ wg.acronym }}

    + +

    + 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 + mailing list (will it remain open or should it be closed). +

    +
    + + {{ form.as_table }} + + + +
    + Back + +
    +
    + +{% endblock %} diff --git a/ietf/templates/wgcharter/date_column.html b/ietf/templates/wgcharter/date_column.html new file mode 100644 index 000000000..515e8d80a --- /dev/null +++ b/ietf/templates/wgcharter/date_column.html @@ -0,0 +1,5 @@ +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} +{% load ietf_filters %}{{ wg.time|date:"Y-m-d" }} + diff --git a/ietf/templates/wgcharter/edit_info.html b/ietf/templates/wgcharter/edit_info.html new file mode 100644 index 000000000..ef0209bc7 --- /dev/null +++ b/ietf/templates/wgcharter/edit_info.html @@ -0,0 +1,102 @@ +{% extends "base.html" %} + +{% block title %} +{% if wg %} +Edit info on {{ wg.acronym }} +{% else %} +Create WG +{% endif %} +{% endblock %} + +{% block morecss %} +form.edit-info #id_name { + width: 396px; +} + +form.edit-info #id_list_email { + width: 396px; +} + +form.edit-info #id_list_subscribe { + width: 396px; +} + +form.edit-info #id_list_archive { + width: 396px; +} + +form.edit-info #id_urls { + width: 400px; +} + +form.edit-info #id_comments { + width: 400px; +} + +{% endblock %} + +{% block pagehead %} + +{% endblock %} + +{% block content %} +{% load ietf_filters %} +

    {% if wg %} +Edit info on {{ wg.acronym }} +{% else %} +Create WG +{% endif %} +

    + +
    + + {% for field in form.visible_fields %} + + + + + {% endfor %} + + + + +
    {{ field.label_tag }}:{{ field }} + {% ifequal field.name "ad" %} + {% if user|in_group:"Area_Director" %} + + {% endif %} + {% endifequal %} + {% if field.help_text %}
    {{ field.help_text }}
    {% endif %} + {{ field.errors }} +
    + {% if wg %} + Back + + {% else %} + + {% endif %} +
    +
    +{% endblock %} + +{% block scripts %} +$(document).ready(function () { + var chairs = eval($("#id_chairs").val()), + secretaries = eval($("#id_secretaries").val()), + techadv = eval($("#id_techadv").val()); + $("#id_chairs").tokenInput("/wgcharter/searchPerson/", { hintText: "", + preventDuplicates: true, + prePopulate: chairs }); + $("#id_secretaries").tokenInput("/wgcharter/searchPerson/", { hintText: "", + preventDuplicates: true, + prePopulate: secretaries }); + $("#id_techadv").tokenInput("/wgcharter/searchPerson/", { hintText: "", + preventDuplicates: true, + prePopulate: techadv }); + $("#id_name").focus(); +}); +{% endblock %} + +{% block content_end %} + +{% endblock %} diff --git a/ietf/templates/wgcharter/edit_position.html b/ietf/templates/wgcharter/edit_position.html new file mode 100644 index 000000000..e9609ef27 --- /dev/null +++ b/ietf/templates/wgcharter/edit_position.html @@ -0,0 +1,71 @@ +{% extends "base.html" %} + +{% block title %}Change position for {{ ad.name }} on {{ wg.acronym }}{% endblock %} + +{% block morecss %} +form.position-form .position ul { + padding: 0; + margin: 0; +} +form.position-form .position li { + list-style-type: none; + float: left; + padding-right: 10px; +} +form.position-form .last-edited { + font-style: italic; +} +form.position-form .block_comment { + padding-top: 20px +} +form.position-form #id_block_comment, +form.position-form #id_comment { + width: 700px; + height: 250px; +} +form.position-form .comment { + margin-top: 20px; +} +{% endblock %} + +{% block content %} +

    Change position for {{ ad.name }} on {{ wg.acronym }}

    + +
    +
    + {{ form.position }} + + + + +
    + +
    + +
    +
    + {{ form.block_comment.label_tag }}: + {% if old_pos and old_pos.block_comment_time %}(last edited {{ old_pos.block_comment_time }}){% endif %} +
    + {{ form.block_comment.errors }} + {{ form.block_comment }} +
    + +
    + {{ form.comment.label_tag }}: + {% if old_pos and old_pos.comment_time %}(last edited {{ old_pos.comment_time }}){% endif %} +
    + {{ form.comment }} + +
    + Back +
    + + {{ form.return_to_url }} + +
    +{% endblock %} + +{% block content_end %} + +{% endblock %} diff --git a/ietf/templates/wgcharter/email_secretariat.txt b/ietf/templates/wgcharter/email_secretariat.txt new file mode 100644 index 000000000..d489456fa --- /dev/null +++ b/ietf/templates/wgcharter/email_secretariat.txt @@ -0,0 +1,5 @@ +{% autoescape off %} +{{ text }} + +WG Record URL: {{ url }} +{% endautoescape %} diff --git a/ietf/templates/wgcharter/in_process.html b/ietf/templates/wgcharter/in_process.html new file mode 100644 index 000000000..e0b628bbc --- /dev/null +++ b/ietf/templates/wgcharter/in_process.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% block title %}Working Groups in IESG process{% endblock %} + +{% block content %} +

    Working Groups in IESG process

    + +

    +Start new WG charter effort +

    + +{% if not recs %} +

    No WGs match your query.

    +{% else %} + + + {% for hdr in meta.hdrs %} + {% include "wgcharter/table_header.html" %} + {% endfor %} + +{% for wg in recs %} +{% include "wgcharter/search_result_row.html" %} +{% endfor %} +
    +{% endif %} +{% endblock content %} diff --git a/ietf/templates/wgcharter/issue_ballot_mail.txt b/ietf/templates/wgcharter/issue_ballot_mail.txt new file mode 100644 index 000000000..3141ed79c --- /dev/null +++ b/ietf/templates/wgcharter/issue_ballot_mail.txt @@ -0,0 +1,38 @@ +{% autoescape off %}To: Internet Engineering Steering Group +From: IESG Secretary +Reply-To: IESG Secretary +Subject: Evaluation: {{ charter.chartered_group }} ({{ charter.chartered_group.acronym }}) + +{% filter wordwrap:73 %}Evaluation for {{ charter.chartered_group }} ({{ charter.chartered_group.acronym }}) can be found at {{ charter_url }} +{% endfilter %} + Please return the full line with your position. + + Yes No Block Abstain +{% for fmt in active_ad_positions %}{{ fmt }} +{% endfor %}{% if inactive_ad_positions %} + +{% for fmt in inactive_ad_positions %}{{ fmt }} +{% endfor %}{% endif %} + +No "Block" positions, are needed for approval. + +BLOCKING AND NON-BLOCKING COMMENTS +================================== +{% filter wordwrap:79 %}{% for p in ad_feedback %}{{ p.ad }}: + +{% if p.block_comment %}Blocking comment [{{ p.time }}]: +{{ p.block_comment }} + +{% endif %}{% if p.comment %}Comment [{{ p.time }}]: +{{ p.comment }} + +{% endif %} +{% endfor %}{% endfilter %} +---- following is a DRAFT of message to be sent AFTER approval --- +{{ approval_text }} + +---- ballot text ---- + +{{ ballot_writeup }} + +{% endautoescape%} diff --git a/ietf/templates/wgcharter/review_text.txt b/ietf/templates/wgcharter/review_text.txt new file mode 100644 index 000000000..4611f2a5a --- /dev/null +++ b/ietf/templates/wgcharter/review_text.txt @@ -0,0 +1,10 @@ +{% load ietf_filters %}{% autoescape off %}From: The IESG +To: IETF-Announce +Subject: WG Review: {{ wg.name }} ({{wg.acronym}}) + +{% filter wordwrap:73 %}{% ifequal review_type "new" %}A new IETF working group has been proposed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal review_type "recharter" %}The {{ wg.name }} ({{wg.acronym}}) working group in the {{ wg.parent.name }} of the IETF is undergoing rechartering.{% endifequal %} The IESG has not made any determination yet. The following draft charter was submitted, and is provided for informational purposes only. Please send your comments to the IESG mailing list (iesg at ietf.org) by {{ info.bydate }}. + +{% include "wgcharter/wg_info.txt" %} + +{% endfilter %} +{% endautoescape %} diff --git a/ietf/templates/wgcharter/search_form.html b/ietf/templates/wgcharter/search_form.html new file mode 100644 index 000000000..d64ea1293 --- /dev/null +++ b/ietf/templates/wgcharter/search_form.html @@ -0,0 +1,144 @@ +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +
    + +
    + {{ form.nameacronym }} +
    +
    + + + + + +
    {{ form.inprocess }} WGs (in chartering process)
    {{ form.active }} WGs (approved charter)
    {{ form.concluded }} WGs (concluded or not under review)
    +
    + + Advanced + +
    + Additional search criteria: + +
    + {{ form.state }} :: {{ form.charter_state }} +
    +
    + {{ form.ad }} +
    +
    + {{ form.area }} +
    +
    + {{ form.anyfield }} +
    +
    + {{ form.eacronym }} +
    +
    + +
    + + + +
    + + + +
    + + diff --git a/ietf/templates/wgcharter/search_main.html b/ietf/templates/wgcharter/search_main.html new file mode 100644 index 000000000..5c3eaf2db --- /dev/null +++ b/ietf/templates/wgcharter/search_main.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% comment %} +Copyright 2011 The IETF Trust. All rights reserved. +{% endcomment %} + +{% block title %}Working Group Charters{% endblock %} + +{% block content %} +

    Working Group Charters

    + +
    +{% include "wgcharter/search_form.html" %} +
    + +
    +{% if meta.searching %} +{% include "wgcharter/search_results.html" %} +{% endif %} +
    +{% endblock content %} +{% block scripts %} +YAHOO.util.Event.onContentReady("search_submit_button", function () { + var oButton = new YAHOO.widget.Button("search_submit_button", {}); +}); +{% endblock scripts %} diff --git a/ietf/templates/wgcharter/search_result_row.html b/ietf/templates/wgcharter/search_result_row.html new file mode 100644 index 000000000..96d2e2528 --- /dev/null +++ b/ietf/templates/wgcharter/search_result_row.html @@ -0,0 +1,13 @@ +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} + + +{{ wg.acronym|safe }} + +{{ wg.name }} +{% include "wgcharter/date_column.html" %} +{% include "wgcharter/status_columns.html" %} + diff --git a/ietf/templates/wgcharter/search_results.html b/ietf/templates/wgcharter/search_results.html new file mode 100644 index 000000000..83e863e74 --- /dev/null +++ b/ietf/templates/wgcharter/search_results.html @@ -0,0 +1,22 @@ +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% if meta.max %} +

    Too many WGs match the query! Returning partial result only.

    +{% endif %} +{% if not recs %} +

    No WGs match your query.

    +{% else %} + + + {% for hdr in meta.hdrs %} + {% include "wgcharter/table_header.html" %} + {% endfor %} + +{% for wg in recs %} +{% include "wgcharter/search_result_row.html" %} +{% endfor %} + +
    +{% endif %} diff --git a/ietf/templates/wgcharter/send_ballot_comment.html b/ietf/templates/wgcharter/send_ballot_comment.html new file mode 100644 index 000000000..9539494f5 --- /dev/null +++ b/ietf/templates/wgcharter/send_ballot_comment.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% load ietf_filters %} +{% block title %}Send ballot position email for {{ ad }}{% endblock %} + +{% block morecss %} +form.send-ballot pre { + margin: 0; + padding: 4px; + border-top: 4px solid #eee; + border-bottom: 4px solid #eee; +} +{% endblock %} + +{% block content %} +

    Send ballot position email for {{ ad }}

    + +
    + + + + + + + + + + + + + + + + +
    From: {{ frm }}
    To: {{ to }}
    Cc:
    + separated
    by comma
    Subject: {{ subject }}
    Body:
    {{ body|wrap_text }}
    + Back + +
    +
    +{% endblock %} diff --git a/ietf/templates/wgcharter/states.html b/ietf/templates/wgcharter/states.html new file mode 100644 index 000000000..e8950bb5b --- /dev/null +++ b/ietf/templates/wgcharter/states.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2011, All Rights Reserved #} + +{% block title %}Charter States{% endblock %} + +{% block morecss %} + .state_column { + width: 13em; + } +{% endblock %} + +{% block content %} + +

    Charter States

    + + + + + + + + + + {% for state in states %} + + + + + {% endfor %} +
    StateDescription
    {{ state.name|escape }}{{ state.desc|escape }}
    + +{% endblock %} diff --git a/ietf/templates/wgcharter/status_columns.html b/ietf/templates/wgcharter/status_columns.html new file mode 100644 index 000000000..462b34f4c --- /dev/null +++ b/ietf/templates/wgcharter/status_columns.html @@ -0,0 +1,17 @@ +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} +{% load ietf_filters ietf_streams %}{% load wg_ballot_icon %} + +{% if wg.charter %} +{{ wg.charter.get_state|safe }} {% ifequal wg.state_id "proposed" %}{% ifnotequal wg.charter.get_state_slug "notrev" %}(Initial Chartering){% endifnotequal %}{% else %}{% ifequal wg.state_id "active" %}{% ifnotequal wg.charter.get_state_slug "approved" %}(Rechartering){% endifnotequal %}{% endifequal %}{% ifequal wg.state_id "conclude" %}(Concluded){% endifequal %}{% endifequal %} +{% else %} +(data missing) +{% endif %} +{% if wg.charter.telechat_date %}
    IESG Telechat: {{ wg.charter.telechat_date }}{% endif %} + +{% block extra_status %}{% endblock %} + + +{% if wg.charter.get_state_slug == "iesgrev" %}{% wg_ballot_icon wg.acronym %}{% endif %} + diff --git a/ietf/templates/wgcharter/submit.html b/ietf/templates/wgcharter/submit.html new file mode 100644 index 000000000..cac82e4cf --- /dev/null +++ b/ietf/templates/wgcharter/submit.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} + +{% block morecss %} +form #id_content { + width: 40em; + height: 600px; +} +{% endblock %} + +{% block title %} +Charter submission for {{ wg.acronym }} +{% endblock %} + +{% block content %} +

    Charter submission for {{ wg.acronym }}

    + +

    The text will be submitted as charter-ietf-{{ wg.acronym }}-{{ next_rev }}

    +
    + + {% for field in form.visible_fields %} + + + + + {% endfor %} + + + + +
    {{ field.label_tag }}: + {{ field }} + {% if field.help_text %}
    {{ field.help_text }}
    {% endif %} + {{ field.errors }} +
    + Back + +
    +
    + +{% endblock %} diff --git a/ietf/templates/wgcharter/table_header.html b/ietf/templates/wgcharter/table_header.html new file mode 100644 index 000000000..604a92536 --- /dev/null +++ b/ietf/templates/wgcharter/table_header.html @@ -0,0 +1,15 @@ +{# Copyright The IETF Trust 2011, All Rights Reserved #} + + + + + {% if hdr.selected %} + + {% else %} + + {% endif %} + + diff --git a/ietf/templates/wgcharter/wg_ballot.html b/ietf/templates/wgcharter/wg_ballot.html new file mode 100644 index 000000000..6fa3e56c7 --- /dev/null +++ b/ietf/templates/wgcharter/wg_ballot.html @@ -0,0 +1,57 @@ +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} +{% load ietf_filters %} + +
    + +{% if user|in_group:"Area_Director,Secretariat" %} +{% if user|in_group:"Area_Director" %} + +{% endif %} +{% if user|in_group:"Secretariat" %} +{% if not info.pos_block %} +{% endif %} +{% endif %} +{% endif %} + +

    Blocking
    +{% with info.pos_block as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    + +

    Yes
    +{% with info.pos_yes as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    + +

    No
    +{% with info.pos_no as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    + +

    Abstain
    +{% with info.pos_abstain as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    + +

    No Record
    +{% for p in info.pos_no_record %} +{{p.name}}{% if user|in_group:"Secretariat" %}{% endif %}
    +{% empty %} +none +{% endfor %} +

    + +
    + +

    Comments

    + +{% for pos in info.positions %} +{% if pos.comment or pos.block_comment %} +

    {{pos.ad|escape}}

    + +{% if pos.block_comment %} +

    Blocking ({{pos.block_comment_time}})

    +
    {{pos.block_comment|fill:"80"|escape }}
    +{% endif %} + +{% if pos.comment %} +

    Comment ({{pos.comment_time}})

    +
    {{pos.comment|fill:"80"|escape }}
    +{% endif %} +{% endif %} +{% endfor %} +
    diff --git a/ietf/templates/wgcharter/wg_ballot_list.html b/ietf/templates/wgcharter/wg_ballot_list.html new file mode 100644 index 000000000..b998547f3 --- /dev/null +++ b/ietf/templates/wgcharter/wg_ballot_list.html @@ -0,0 +1,6 @@ +{% load ietf_filters %} +{% for p in positions %} +{% for oad in info.old_ads %}{% ifequal oad p.ad %}[{%endifequal%}{% endfor %}{{p.ad}}{% for oad in info.old_ads %}{% ifequal oad p.ad %}]{%endifequal%}{% endfor %}{% if p.comment or p.block_comment %} *{% endif %}
    +{% empty %} +none +{% endfor %} diff --git a/ietf/templates/wgcharter/wg_description.html b/ietf/templates/wgcharter/wg_description.html new file mode 100644 index 000000000..3084834d0 --- /dev/null +++ b/ietf/templates/wgcharter/wg_description.html @@ -0,0 +1 @@ +IETF Working Group {{ wg.name }} ({{ wg.acronym }}){% if wg.parent %} under the {{ wg.parent }}{% endif %} diff --git a/ietf/templates/wgcharter/wg_history.html b/ietf/templates/wgcharter/wg_history.html new file mode 100644 index 000000000..d4af98e93 --- /dev/null +++ b/ietf/templates/wgcharter/wg_history.html @@ -0,0 +1,40 @@ +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} + + + +{% for c in history %} + + + +{% if c.is_rev %} + + + +{% endif %} + +{% if c.is_com %} + + + +{% endif %} + + +{% endfor %} + +
    DateVersionByText
    {{ c.date|date:"Y-m-d" }}{{ c.info.version }}(System)New version available: charter-ietf-{{ c.group.acronym }}-{{ c.charter.rev }} {% if c.prev_charter %}(diff from -{{ c.prev_charter.rev }}){% endif %}{{ c.info.version }}{{ c.info.by|escape }}{% if c.comment.ballot %} +[Ballot {{ c.comment.get_ballot_display }}]
    +{% endif %} +{% if c.info.snipped %} +
    {{ c.info.textSnippet|safe }}
    +[show all] + +{% else %} +{{ c.info.text|fill:"80"|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }} +{% endif %} +
    diff --git a/ietf/templates/wgcharter/wg_info.txt b/ietf/templates/wgcharter/wg_info.txt new file mode 100644 index 000000000..3818ed4ec --- /dev/null +++ b/ietf/templates/wgcharter/wg_info.txt @@ -0,0 +1,24 @@ +{{ wg.name }} ({{ wg.acronym }}) +------------------------------------------------ +Current Status: {{ wg.state.name }} Working Group + +Chairs: +{% for p in info.chairs %} {{ p.name }} <{{p.email}}> +{% endfor %} +Secretaries: +{% for p in info.secr %} {{ p.name }} <{{p.email}}> +{% endfor %} +Technical advisors: +{% for p in info.techadv %} {{ p.name }} <{{p.email}}> +{% endfor %} +Assigned Area Director: + {{ info.ad.0.name }} <{{ info.ad.0.email }}> + +Mailing list: + Address: {{ info.list.0 }} + To Subscribe: {{ info.list_subscribe.0 }} + Archive: {{ info.list_archive.0 }} + +Charter: + +{{ info.charter_txt }} diff --git a/ietf/templates/wgcharter/wg_main.html b/ietf/templates/wgcharter/wg_main.html new file mode 100644 index 000000000..793aa9b20 --- /dev/null +++ b/ietf/templates/wgcharter/wg_main.html @@ -0,0 +1,72 @@ +{% extends "base.html" %} + +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} +{% block morecss %} +.metabox { width: 99%; margin-top:8px; padding:4px; margin-bottom:1em; } +#metatable { border: 0; border-spacing: 0; } +#metatable tr { vertical-align:top ;} +.comment_toggle { text-decoration: underline; color: blue; } +.comment_date { white-space: nowrap; } + +div.diffTool { padding: 8px 4px; margin: 8px 0;} +.diffTool label { float:left; width:50px; } + +.markup_draft pre {line-height: 1.2em; margin: 0; } +.m_hdr, .m_ftr { color: #808080; } +.m_ftr { border-bottom: 1px solid #a0a0a0; } +.m_h { font-family: arial; font-weight:bold;} +.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;} +{% endblock %} + +{% block pagehead %} + + +{% endblock %} + +{% block title %}{% include "wgcharter/wg_title.html" %}{% endblock title %} + +{% block content %} +

    {% include "wgcharter/wg_title.html" %} +{% ifequal wg.state_id "conclude" %}
    (concluded WG){% endifequal %} +{% ifequal wg.state_id "proposed" %}
    (proposed WG){% endifequal %} +

    + +
    + +
    + +{% block tab_content %}{% endblock %} + +
    +
    + +{% endblock content %} + +{% block scripts %} +function toggleComment(n) { + var el = document.getElementById("commentF"+n); + var el2 = document.getElementById("commentS"+n); + var el3 = document.getElementById("commentT"+n); + if (el.style.display == 'none') { + el.style.display = 'block'; + el2.style.display = 'none'; + el3.innerHTML = ""; //[hide]"; + } else { + el.style.display = 'none'; + el2.style.display= 'block'; + el3.innerHTML = "[show all]"; + } +} +{% endblock scripts %} diff --git a/ietf/templates/wgcharter/wg_tab_ballot.html b/ietf/templates/wgcharter/wg_tab_ballot.html new file mode 100644 index 000000000..09bf718f5 --- /dev/null +++ b/ietf/templates/wgcharter/wg_tab_ballot.html @@ -0,0 +1,11 @@ +{% extends "wgcharter/wg_main.html" %} +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} + +{% block tab_content %} +{% include "wgcharter/wg_ballot.html" %} +{% endblock tab_content %} + diff --git a/ietf/templates/wgcharter/wg_tab_base.html b/ietf/templates/wgcharter/wg_tab_base.html new file mode 100644 index 000000000..c55982e5b --- /dev/null +++ b/ietf/templates/wgcharter/wg_tab_base.html @@ -0,0 +1,59 @@ +{% extends "wgcharter/wg_main.html" %} + +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} + +{% block tab_content %} +{% block wg_revision %}{% endblock %} +
    + +{% block wg_metatable %}{% endblock %} +
    + +
    +{% block wg_metalinks %}{% endblock %} +
    + +{% block wg_metabuttons %} +{% if user|in_group:"Area_Director,Secretariat" %} +{% if not snapshot %} +
    +{% ifnotequal wg.charter.get_state_slug "notrev" %} +{% ifnotequal wg.charter.get_state_slug "approved" %} +Abandon effort +Change state +{% ifnotequal wg.state_id "conclude" %} +Edit charter +{% endifnotequal %} +{% else %} +Recharter +{% endifnotequal %} +{% else %} +Recharter +{% endifnotequal %} + +{% ifequal wg.state_id "active" %}{% ifequal wg.charter.get_state_slug "approved" %} +Conclude WG +{% endifequal %}{% endifequal %} + +{% ifnotequal wg.state_id "conclude" %} +Edit WG +{% endifnotequal %} + + +
    +{% endif %}{# if not snapshot #} +{% endif %}{# if user in group #} +{% endblock wg_metabuttons%} +
    + +
    +{% block charter_text %}{% endblock %} +
    +{% endblock tab_content %} + +{% block content_end %} +{% endblock content_end %} diff --git a/ietf/templates/wgcharter/wg_tab_charter.html b/ietf/templates/wgcharter/wg_tab_charter.html new file mode 100644 index 000000000..666f163f1 --- /dev/null +++ b/ietf/templates/wgcharter/wg_tab_charter.html @@ -0,0 +1,81 @@ +{% extends "wgcharter/wg_tab_base.html" %} + +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} + +{% block wg_revision %} +Snapshots: {% for d in versions reversed %}{% if forloop.first %}{% ifnotequal rev wg.charter.rev %}{% endifnotequal %}{% else %}{% ifnotequal rev d.rev %}{% ifequal d.rev wg.charter.rev %}{% else %}{% endifequal %}{% endifnotequal %}{% endif %}{{ d.rev }}{% ifnotequal rev d.rev %}{% endifnotequal %}{% if forloop.first %}{% ifnotequal rev wg.charter.rev %}{% endifnotequal %}{% endif %} {% endfor %} +{% endblock %} + +{% block wg_metatable %} +WG name:{% if not snapshot %}{{ wg.name }}{% else %} {{ gh.name }} {% endif %} +WG acronym:{% if not snapshot %}{{ wg.acronym }} {% if info.prev_acronyms %}(previous acronyms: {% for a in info.prev_acronyms %}{{ a }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}{% else %}{{ gh.acronym }}{% endif %} +IETF area:{% if not snapshot %}{{ wg.parent|default:"-" }}{% else %}{{ gh.parent|default:"-" }}{% endif %} + +
    + +WG chairs:{% if not snapshot %}{% for n in info.chairs %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}{% else %}{% for n in info.history_chairs %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endif %} +{% if not snapshot %} +WG secretaries:{% for n in info.secr %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %} +WG technical advisors:{% for n in info.techadv %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %} +{% endif %} +Assigned AD:{% if not snapshot %}{{ wg.ad }}{% else %}{{ gh.ad }}{% endif %} + +{% if not snapshot %} +
    + +Mailing list:{{ wg.list_email }}{% if info.prev_list_email %}
    (previous lists: {% for a in info.prev_list_email %}{{ a }}{% if not forloop.last %}, {% else %}){% endif %}{% endfor %}{% endif %} +Mailing list subscribe:{{ wg.list_subscribe }}{% if info.prev_list_subscribe %}
    (previous archives: {% for a in info.prev_list_subscribe %}{{ a }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %} +Mailing list archive:{{ wg.list_archive }}{% if info.prev_list_archive %}
    (previous archives: {% for a in info.prev_list_archive %}{{ a }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %} +Other web sites:{% for a in wg.groupurl_set.all %}{{ a.url }} {% if a.name %}({{ a.name }}){% endif %}{% if not forloop.last %}
    {% endif %}{% endfor %} + +
    + +WG State: {{ wg.state|safe }} +{% ifnotequal wg.state_id "conclude" %}Charter State: {{ wg.charter.get_state|safe }} {% ifequal wg.state_id "proposed" %}{% ifnotequal wg.charter.get_state_slug "notrev" %}(Initial Chartering){% endifnotequal %}{% else %}{% ifnotequal wg.charter.get_state_slug "approved" %}(Rechartering){% endifnotequal %}{% endifequal %}{% endifnotequal %} +{% ifequal wg.state_id "proposed" %} +{% if wg.comments %} +Reason for chartering:{{ wg.comments }} +{% endif %} +{% endifequal %} +{% ifequal wg.state_id "active" %} +{% ifnotequal wg.charter.get_state_slug "approved" %} +{% if wg.comments %} +Reason for rechartering:{{ wg.comments }} +{% endif %} +{% endifnotequal %} +{% endifequal %} + +
    + +Last updated: {{ info.last_update|date:"Y-m-d"|default:"(data missing)" }} +
    + +{% endif %}{# if not snapshot #} + +{% endblock wg_metatable %} + +{% block wg_metalinks %} +{% if not snapshot %} +
    Atom feed +{% endif %} +{% endblock %} + +{% block charter_text %} +{% ifnotequal charter.rev "" %} +

    Other versions: plain text

    + +

    Charter {{ charter.name }}-{{ rev }}

    + +
    +{{ content|fill:"80"|safe|linebreaksbr|keep_spacing|sanitize_html|safe }} +
    + +{% else %} +

    The WG does not yet have a charter

    +{% endifnotequal %} +{% endblock %}{# charter_text #} + diff --git a/ietf/templates/wgcharter/wg_tab_history.html b/ietf/templates/wgcharter/wg_tab_history.html new file mode 100644 index 000000000..9df8bab17 --- /dev/null +++ b/ietf/templates/wgcharter/wg_tab_history.html @@ -0,0 +1,18 @@ +{% extends "wgcharter/wg_main.html" %} +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} + +{% block tab_content %} +{% include "wgcharter/charter_diffs.html" %} +

    WG History

    +{% if user|in_group:"Area_Director,Secretariat" %} + +{% endif %} + +{% include "wgcharter/wg_history.html" %} +{% endblock tab_content %} diff --git a/ietf/templates/wgcharter/wg_tab_writeup.html b/ietf/templates/wgcharter/wg_tab_writeup.html new file mode 100644 index 000000000..2a3814eb3 --- /dev/null +++ b/ietf/templates/wgcharter/wg_tab_writeup.html @@ -0,0 +1,52 @@ +{% extends "wgcharter/wg_main.html" %} +{% comment %} +Copyright The IETF Trust 2011, All Rights Reserved +{% endcomment %} + +{% load ietf_filters %} + +{% block tab_content %} +

    WG Review Announcement

    +{% if user|in_group:"Area_Director,Secretariat" %} +
    +

    + +Edit WG Review Announcement + +

    +{% endif %} +
    +{{ info.review_text|escape|urlize }}
    +
    +
    + +

    WG Action Announcement

    +{% if user|in_group:"Area_Director,Secretariat" %} +
    +

    + +Edit WG Action Announcement + +

    +{% endif %} +
    +{{ info.action_text|escape|urlize }}
    +
    + +
    + +

    Ballot Announcement

    +{% if user|in_group:"Area_Director,Secretariat" %} +
    +

    + +Edit Ballot Announcement + +

    +{% endif %} +
    +{{ info.ballot_text|escape|urlize }}
    +
    + +
    +{% endblock tab_content %} diff --git a/ietf/templates/wgcharter/wg_title.html b/ietf/templates/wgcharter/wg_title.html new file mode 100644 index 000000000..3b101b9e0 --- /dev/null +++ b/ietf/templates/wgcharter/wg_title.html @@ -0,0 +1 @@ +{{ wg.name }} ({{wg.acronym}}) diff --git a/ietf/templates/wginfo/wg_base.html b/ietf/templates/wginfo/wg_base.html index 853c3368e..26dfea424 100644 --- a/ietf/templates/wginfo/wg_base.html +++ b/ietf/templates/wginfo/wg_base.html @@ -49,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .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;} {% endblock morecss %} {% block content %} -
    -

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

    +
    +

    {{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 %} | diff --git a/ietf/templates/wginfo/wg_charterREDESIGN.html b/ietf/templates/wginfo/wg_charterREDESIGN.html index 1fb1e2c5e..563eeabb6 100644 --- a/ietf/templates/wginfo/wg_charterREDESIGN.html +++ b/ietf/templates/wginfo/wg_charterREDESIGN.html @@ -59,7 +59,7 @@ is occasionally incorrect. Area Director: {% if not wg.ad %}?{% else %} -{{ wg.ad.name }} <{{ wg.ad_email }}>{% endif %} +{{ wg.ad.name }} <{{ wg.areadirector.address }}>{% endif %} {% if wg.techadvisors %} @@ -115,7 +115,7 @@ is occasionally incorrect. {% 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.description}}{% if not forloop.last %}, {% endif %} +{{ url.name }}{% if not forloop.last %}, {% endif %} {% endfor %}

    {% endif %} diff --git a/ietf/urls.py b/ietf/urls.py index ce66a6c8b..56285d3a8 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -9,6 +9,7 @@ from ietf.idtracker.feeds import DocumentComments, InLastCall from ietf.ipr.feeds import LatestIprDisclosures from ietf.proceedings.feeds import LatestWgProceedingsActivity from ietf.liaisons.feeds import Liaisons +from ietf.wgcharter.feeds import GroupComments from ietf.idtracker.sitemaps import IDTrackerMap, DraftMap from ietf.liaisons.sitemaps import LiaisonMap @@ -24,6 +25,7 @@ feeds = { 'iesg-agenda': IESGAgenda, 'last-call': InLastCall, 'comments': DocumentComments, + 'wgcomments': GroupComments, 'ipr': LatestIprDisclosures, 'liaison': Liaisons, 'wg-proceedings' : LatestWgProceedingsActivity @@ -59,6 +61,7 @@ urlpatterns = patterns('', (r'^accounts/', include('ietf.ietfauth.urls')), (r'^doc/', include('ietf.idrfc.urls')), (r'^wg/', include('ietf.wginfo.urls')), + (r'^wgcharter/', include('ietf.wgcharter.urls')), (r'^cookies/', include('ietf.cookies.urls')), (r'^submit/', include('ietf.submit.urls')), (r'^streams/', include('ietf.ietfworkflows.urls')), diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index e01cd207b..2e1f78d41 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -10,6 +10,14 @@ from redesign.group.models import * from redesign.person.models import * def make_test_data(): + # telechat dates + t = datetime.date.today() + old = TelechatDate.objects.create(date=t - datetime.timedelta(days=14)).date + date1 = TelechatDate.objects.create(date=t).date + date2 = TelechatDate.objects.create(date=t + datetime.timedelta(days=14)).date + date3 = TelechatDate.objects.create(date=t + datetime.timedelta(days=14 * 2)).date + date4 = TelechatDate.objects.create(date=t + datetime.timedelta(days=14 * 3)).date + # groups secretariat = Group.objects.create( name="Secretariat", @@ -36,6 +44,13 @@ def make_test_data(): state_id="active", type_id="area", parent=ietf) + individ = Group.objects.create( + name="Individual submissions", + acronym="none", + state_id="active", + type_id="individ", + parent=None) + # mars WG group = Group.objects.create( name="Martian Special Interest Group", acronym="mars", @@ -43,13 +58,52 @@ def make_test_data(): type_id="wg", parent=area, ) - individ = Group.objects.create( - name="Individual submissions", - acronym="none", - state_id="active", - type_id="individ", - parent=None) - + charter = Document.objects.create( + name="charter-ietf-" + group.acronym, + type_id="charter", + title=group.name, + group=group, + rev="00", + ) + charter.set_state(State.objects.get(slug="approved", type="charter")) + group.charter = charter + group.save() + DocAlias.objects.create( + name=charter.name, + document=charter + ) + # ames WG + group = Group.objects.create( + name="Asteroid Mining Equipment Standardization Group", + acronym="ames", + state_id="proposed", + type_id="wg", + parent=area, + ) + charter = Document.objects.create( + name="charter-ietf-" + group.acronym, + type_id="charter", + title=group.name, + group=group, + rev="00", + ) + charter.set_state(State.objects.get(slug="infrev", type="charter")) + DocAlias.objects.create( + name=charter.name, + document=charter + ) + 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 # system @@ -109,6 +163,20 @@ def make_test_data(): group=area, person=p, email=email) + else: + areahist = GroupHistory.objects.create( + group=area, + name=area.name, + acronym=area.acronym, + type_id=area.type_id, + state_id=area.state_id, + parent=area.parent + ) + RoleHistory.objects.create( + name_id="ad", + group=areahist, + person=p, + email=email) # group chair u = User.objects.create(username="marschairman") @@ -224,33 +292,7 @@ def make_test_data(): rev="00", ) - # telechat dates - t = datetime.date.today() - old = TelechatDate.objects.create(date=t - datetime.timedelta(days=14)).date - date1 = TelechatDate.objects.create(date=t).date - date2 = TelechatDate.objects.create(date=t + datetime.timedelta(days=14)).date - date3 = TelechatDate.objects.create(date=t + datetime.timedelta(days=14 * 2)).date - date4 = TelechatDate.objects.create(date=t + datetime.timedelta(days=14 * 3)).date - - # WG Actions - group = Group.objects.create( - name="Asteroid Mining Equipment Standardization Group", - acronym="ames", - state_id="proposed", - type_id="wg", - parent=area, - ) - WGAction.objects.create( - pk=group.pk, - note="", - status_date=datetime.date.today(), - agenda=1, - token_name="Aread", - category=13, - telechat_date=date2 - ) - - # Meeting + # meeting Meeting.objects.create( number="42", type_id="ietf", diff --git a/ietf/wgchairs/forms.py b/ietf/wgchairs/forms.py index cc59a98bb..449c82aa1 100644 --- a/ietf/wgchairs/forms.py +++ b/ietf/wgchairs/forms.py @@ -21,6 +21,7 @@ from workflows.models import Transition from redesign.doc.models import WriteupDocEvent from redesign.person.models import Person, Email from redesign.group.models import Role, RoleName +from redesign.group.utils import save_group_in_history from redesign.name.models import DocTagName @@ -45,7 +46,7 @@ class RelatedWGForm(forms.Form): class TagForm(RelatedWGForm): - tags = forms.ModelMultipleChoiceField(AnnotationTag.objects.filter(wgworkflow__name='Default WG Workflow'), + tags = forms.ModelMultipleChoiceField(AnnotationTag.objects.filter(workflow__name='Default WG Workflow'), widget=forms.CheckboxSelectMultiple, required=False) def save(self): @@ -58,7 +59,7 @@ class TagForm(RelatedWGForm): class StateForm(RelatedWGForm): - states = forms.ModelMultipleChoiceField(State.objects.filter(wgworkflow__name='Default WG Workflow'), + states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow'), widget=forms.CheckboxSelectMultiple, required=False) def update_transitions(self, workflow): @@ -103,7 +104,7 @@ class DeleteTransitionForm(RelatedWGForm): class TransitionForm(forms.ModelForm): - states = forms.ModelMultipleChoiceField(State.objects.filter(wgworkflow__name='Default WG Workflow')) + states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow')) class Meta: model = Transition @@ -182,6 +183,7 @@ class RemoveDelegateForm(RelatedWGForm): def save(self): delegates = self.cleaned_data.get('delete') + save_group_in_history(self.wg) WGDelegate.objects.filter(pk__in=delegates).delete() self.set_message('success', 'Delegates removed') @@ -281,7 +283,9 @@ class AddDelegateForm(RelatedWGForm): 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): - delegate, created = Role.objects.get_or_create( + created = True + save_group_in_history(self.wg) + 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, diff --git a/ietf/wgchairs/tests.py b/ietf/wgchairs/tests.py index ce46ccffe..464ae37a7 100644 --- a/ietf/wgchairs/tests.py +++ b/ietf/wgchairs/tests.py @@ -13,7 +13,7 @@ from ietf.utils.mail import outbox if settings.USE_DB_REDESIGN_PROXY_CLASSES: from redesign.person.models import Person, Email - from redesign.group.models import Group, Role, GroupStateTransitions + from redesign.group.models import Group, GroupHistory, Role, GroupStateTransitions from redesign.doc.models import Document, State, WriteupDocEvent from redesign.name.models import DocTagName @@ -87,6 +87,7 @@ class ManageDelegatesTestCase(django.test.TestCase): 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")) @@ -94,7 +95,9 @@ class ManageDelegatesTestCase(django.test.TestCase): q = PyQuery(r.content) self.assertTrue("new delegate" in r.content) self.assertTrue(Email.objects.get(address="plain@example.com").person.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'] diff --git a/ietf/wgcharter/__init__.py b/ietf/wgcharter/__init__.py new file mode 100644 index 000000000..792d60054 --- /dev/null +++ b/ietf/wgcharter/__init__.py @@ -0,0 +1 @@ +# diff --git a/ietf/wgcharter/feeds.py b/ietf/wgcharter/feeds.py new file mode 100644 index 000000000..faa5c7089 --- /dev/null +++ b/ietf/wgcharter/feeds.py @@ -0,0 +1,67 @@ +# Copyright The IETF Trust 2011, All Rights Reserved + +from django.conf import settings +from django.contrib.syndication.feeds import Feed, FeedDoesNotExist +from django.utils.feedgenerator import Atom1Feed +from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse +from ietf.utils.history import find_history_active_at + +from redesign.group.models import Group +from ietf.wgcharter.views import _get_history, _get_html +from wgcharter import markup_txt +import datetime +import re, os + +class GroupComments(Feed): + feed_type = Atom1Feed + title_template = "feeds/wg_charter_title.html" + description_template = "feeds/wg_charter_description.html" + def get_object(self, bits): + if len(bits) != 1: + raise ObjectDoesNotExist + return Group.objects.get(acronym=bits[0]) + + def title(self, obj): + return "WG Record changes for %s" % obj.acronym + + def link(self, obj): + if obj is None: + raise FeedDoesNotExist + return reverse('wg_view', kwargs={'name': obj.acronym}) + + def description(self, obj): + return self.title(obj) + + def items(self, obj): + history = _get_history(obj) + for h in history: + gh = find_history_active_at(obj, h['date']) + if gh: + h['chairs'] = [x.email.person.name for x in gh.rolehistory_set.filter(name__slug="chair")] + h['secr'] = [x.email.person.name for x in gh.rolehistory_set.filter(name__slug="secr")] + h['techadv'] = [x.email.person.name for x in gh.rolehistory_set.filter(name__slug="techadv")] + else: + h['chairs'] = [x.email.person.name for x in obj.role_set.filter(name__slug="chair")] + h['secr'] = [x.email.person.name for x in obj.role_set.filter(name__slug="secr")] + h['techadv'] = [x.email.person.name for x in obj.role_set.filter(name__slug="techadv")] + dh = find_history_active_at(obj.charter, h['date']) + if dh: + h['rev'] = dh.rev + h['charter'] = _get_html( + str(dh.name)+"-"+str(dh.rev)+",html", + os.path.join(dh.get_file_path(), dh.name+"-"+dh.rev+".txt")) + else: + h['rev'] = obj.charter.rev + h['charter'] = _get_html( + "charter-ietf-"+str(obj.acronym)+"-"+str(obj.charter.rev)+",html", + os.path.join(obj.charter.get_file_path(), "charter-ietf-"+obj.acronym+"-"+obj.charter.rev+".txt")) + return history + + def item_link(self, obj): + return reverse('wg_view', kwargs={'name': obj['group'].acronym}) + + def item_pubdate(self, obj): + return obj['date'] + + diff --git a/ietf/wgcharter/mails.py b/ietf/wgcharter/mails.py new file mode 100644 index 000000000..cab79d4c3 --- /dev/null +++ b/ietf/wgcharter/mails.py @@ -0,0 +1,114 @@ +# generation of mails + +import textwrap +from datetime import datetime, date, time, timedelta + +from django.template.loader import render_to_string +from django.utils.html import strip_tags +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse + +from ietf.utils.mail import send_mail, send_mail_text +from ietf.idtracker.models import * +from ietf.ipr.search import iprs_from_docs +from redesign.doc.models import WriteupDocEvent, DocAlias, GroupBallotPositionDocEvent +from redesign.person.models import Person + +# These become part of the subject of the email +types = {} +types['state'] = "State changed" +types['state-notrev'] = "State changed to Not currently under review" +types['state-infrev'] = "State changed to Informal review" +types['state-intrev'] = "State changed to Internal review" +types['state-extrev'] = "State changed to External review" +types['state-iesgrev'] = "State changed to IESG review" +types['state-approved'] = "Charter approved" +types['conclude'] = "Request closing of WG" + +def email_secretariat(request, wg, type, text): + to = ["iesg-secretary@ietf.org"] + + text = strip_tags(text) + send_mail(request, to, None, + "Regarding WG %s: %s" % (wg.acronym, types[type]), + "wgcharter/email_secretariat.txt", + dict(text=text, + url=settings.IDTRACKER_BASE_URL + urlreverse('wg_view', kwargs=dict(name=wg.acronym)))) + +def generate_ballot_writeup(request, doc): + e = WriteupDocEvent() + e.type = "changed_ballot_writeup_text" + e.by = request.user.get_profile() + e.doc = doc + e.desc = u"Ballot writeup was generated" + e.text = unicode(render_to_string("wgcharter/ballot_writeup.txt")) + e.save() + + return e + +def generate_issue_ballot_mail(request, charter): + active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct() + + e = charter.latest_event(type="started_iesg_process") + seen = [] + positions = [] + for p in GroupBallotPositionDocEvent.objects.filter(doc=charter, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad'): + if p.ad not in seen: + positions.append(p) + seen.append(p.ad) + + # format positions and setup blocking and non-blocking comments + ad_feedback = [] + seen = set() + active_ad_positions = [] + inactive_ad_positions = [] + for p in positions: + if p.ad in seen: + continue + + seen.add(p.ad) + + def formatted(val): + if val: + return "[ X ]" + else: + return "[ ]" + + fmt = u"%-21s%-6s%-6s%-8s%-7s" % ( + p.ad.name, + formatted(p.pos_id == "yes"), + formatted(p.pos_id == "no"), + formatted(p.pos_id == "block"), + formatted(p.pos_id == "abstain"), + ) + + if p.ad in active_ads: + active_ad_positions.append(fmt) + if not p.pos_id == "block": + p.block_comment = "" + if p.comment or p.block_comment: + ad_feedback.append(p) + else: + inactive_ad_positions.append(fmt) + + active_ad_positions.sort() + inactive_ad_positions.sort() + ad_feedback.sort(key=lambda p: p.ad.name) + + e = charter.latest_event(WriteupDocEvent, type="changed_action_announcement") + approval_text = e.text if e else "" + + e = charter.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text") + ballot_writeup = e.text if e else "" + + return render_to_string("wgcharter/issue_ballot_mail.txt", + dict(charter=charter, + charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(), + active_ad_positions=active_ad_positions, + inactive_ad_positions=inactive_ad_positions, + ad_feedback=ad_feedback, + approval_text=approval_text, + ballot_writeup=ballot_writeup, + ) + ) + diff --git a/ietf/wgcharter/markup_txt.py b/ietf/wgcharter/markup_txt.py new file mode 100644 index 000000000..7fa7ff5c9 --- /dev/null +++ b/ietf/wgcharter/markup_txt.py @@ -0,0 +1,66 @@ +# 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.utils.html import escape +import string +import re + +def markup(content): + # normalize line endings to LF only + content = content.replace("\r\n", "\n") + content = content.replace("\r", "\n") + + # at this point, "content" is normal string + # fix most common non-ASCII characters + t1 = string.maketrans("\x91\x92\x93\x94\x95\x96\x97\xc6\xe8\xe9", "\'\'\"\"o--\'ee") + # map everything except printable ASCII, TAB, LF, FF to "?" + t2 = string.maketrans('','') + t3 = "?"*9 + "\t\n?\f" + "?"*19 + t2[32:127] + "?"*129 + t4 = t1.translate(t3) + content = content.translate(t4) + + # remove leading white space + content = content.lstrip() + # remove runs of blank lines + content = re.sub("\n\n\n+", "\n\n", content) + + # expand tabs + escape + content = escape(content.expandtabs()) + + content = re.sub("\n(.+\[Page \d+\])\n\f\n(.+)\n", """\n\g<1>\n\g<2>\n""", content) + content = re.sub("\n(.+\[Page \d+\])\n\s*$", """\n\g<1>\n""", content) + # remove remaining FFs (to be valid XHTML) + content = content.replace("\f","\n") + + content = re.sub("\n\n([0-9]+\\.|[A-Z]\\.[0-9]|Appendix|Status of|Abstract|Table of|Full Copyright|Copyright|Intellectual Property|Acknowled|Author|Index)(.*)(?=\n\n)", """\n\n\g<1>\g<2>""", content) + + return "
    "+content+"
    \n" diff --git a/ietf/wgcharter/models.py b/ietf/wgcharter/models.py new file mode 100644 index 000000000..e69de29bb diff --git a/ietf/wgcharter/templatetags/__init__.py b/ietf/wgcharter/templatetags/__init__.py new file mode 100644 index 000000000..792d60054 --- /dev/null +++ b/ietf/wgcharter/templatetags/__init__.py @@ -0,0 +1 @@ +# diff --git a/ietf/wgcharter/templatetags/wg_ballot_icon.py b/ietf/wgcharter/templatetags/wg_ballot_icon.py new file mode 100644 index 000000000..712e36e4f --- /dev/null +++ b/ietf/wgcharter/templatetags/wg_ballot_icon.py @@ -0,0 +1,124 @@ +# Copyright The IETF Trust 2011, All Rights Reserved + +from django import template +from django.core.urlresolvers import reverse as urlreverse +from django.conf import settings +from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days +from redesign.doc.models import GroupBallotPositionDocEvent +from redesign.person.models import Person +from redesign.group.models import Group + +register = template.Library() + +def get_user_adid(context): + if 'user' in context and in_group(context['user'], "Area_Director"): + return context['user'].get_profile().id + else: + return None + +def get_user_name(context): + if 'user' in context and context['user'].is_authenticated(): + from person.models import Person + try: + return context['user'].get_profile().name + except Person.DoesNotExist: + return None + +def render_ballot_icon(context, name): + wg = Group.objects.get(acronym=name) + doc = wg.charter + adId = get_user_adid(context) + red = 0 + green = 0 + yellow = 0 + gray = 0 + blank = 0 + my = None + + active_ads = list(Person.objects.filter(email__role__name="ad", + email__role__group__type="area", + email__role__group__state="active").distinct()) + started_process = doc.latest_event(type="started_iesg_process") + latest_positions = [] + for p in active_ads: + p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=doc, ad=p).order_by("-time")) + if p_pos != []: + latest_positions.append(p_pos[0]) + for p in latest_positions: + if not p.pos_id: + blank = blank + 1 + elif (p.pos_id == "yes"): + green = green + 1 + elif (p.pos_id == "no"): + green = green + 1 + elif (p.pos_id == "block"): + red = red + 1 + elif (p.pos_id == "abstain"): + yellow = yellow + 1 + else: + blank = blank + 1 + if adId and (p.ad_id == adId): + my = p.pos.name + return render_ballot_icon2(wg.acronym, red,yellow,green,gray,blank, my, adId)+"" + +def render_ballot_icon2(acronym, red,yellow,green,gray,blank, my, adId): + edit_position_url = urlreverse('wg_edit_position', kwargs=dict(name=acronym)) + if adId: + res_cm = ' oncontextmenu="editWGBallot(\''+str(edit_position_url)+'\');return false;"' + else: + res_cm = '' + res = '' + for y in range(3): + res = res + "" + for x in range(5): + myMark = False + if red > 0: + c = "ballot_icon_red" + red = red - 1 + myMark = (my == "Block") + elif yellow > 0: + c = "ballot_icon_yellow" + yellow = yellow - 1 + myMark = (my == "Abstain") + elif green > 0: + c = "ballot_icon_green" + green = green - 1 + myMark = (my == "Yes") or (my == "No") + else: + c = "" + myMark = (y == 2) and (x == 4) and (my == "No Record") + if myMark: + res = res + '' + res = res + '
    ' + my = None + else: + res = res + '' + res = res + '
    ' + return res + + +class BallotIconNode(template.Node): + def __init__(self, doc_var): + self.doc_var = doc_var + def render(self, context): + doc = template.resolve_variable(self.doc_var, context) + return render_ballot_icon(context, doc) + +def do_ballot_icon(parser, token): + try: + tagName, docName = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] + return BallotIconNode(docName) + +register.tag('wg_ballot_icon', do_ballot_icon) + +@register.filter +def my_position(doc, user): + user_name = get_user_name({'user':user}) + p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=doc, ad=Person.objects.get(user__name=user_name)).order_by("-time")) + if p_pos == []: + return "No record" + else: + return p_pos[0].pos.name + diff --git a/ietf/wgcharter/tests.py b/ietf/wgcharter/tests.py new file mode 100644 index 000000000..f910de574 --- /dev/null +++ b/ietf/wgcharter/tests.py @@ -0,0 +1,559 @@ +# Copyright The IETF Trust 2011, All Rights Reserved + +import os, shutil, datetime +from StringIO import StringIO + +import django.test +from django.conf import settings +from ietf.utils.mail import outbox +from ietf.utils.test_data import make_test_data +from ietf.utils.test_utils import login_testing_unauthorized + +from pyquery import PyQuery + +from redesign.doc.models import * +from redesign.group.models import * +from redesign.group.utils import * +from redesign.name.models import * +from redesign.person.models import * +from ietf.iesg.models import TelechatDate + +from utils import * + +class SearchTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_search(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + group.charter.set_state(State.objects.get(slug="infrev", type="charter")) + + r = self.client.get("/wgcharter/") + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search")) + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search_in_process")) + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search_by_area", kwargs=dict(name=group.parent.acronym))) + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=%s" % group.name.replace(" ", "+")) + self.assertEquals(r.status_code, 302) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something") + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=acronym&acronym=some") + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=state&state=active&charter_state=") + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=state&state=&charter_state=%s" % State.objects.get(type="charter", slug="approved").pk) + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=ad&ad=%s" % Person.objects.get(name="Aread Irector").pk) + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=area&area=%s" % group.parent.pk) + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=anyfield&anyfield=something") + self.assertEquals(r.status_code, 200) + + r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=eacronym&eacronym=someold") + self.assertEquals(r.status_code, 200) + +class WgStateTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_change_state(self): + make_test_data() + + group = Group.objects.get(acronym="ames") + charter = group.charter + + # -- Test change state -- + url = urlreverse('wg_change_state', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + first_state = charter.get_state() + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form select[name=charter_state]')), 1) + + # faulty post + r = self.client.post(url, dict(charter_state="-12345")) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + self.assertEquals(charter.get_state(), first_state) + + # change state + for slug in ("intrev", "extrev", "iesgrev", "approved"): + s = State.objects.get(type="charter", slug=slug) + events_before = charter.docevent_set.count() + mailbox_before = len(outbox) + + r = self.client.post(url, dict(state="active", charter_state=s.pk, message="test message")) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name="charter-ietf-%s" % group.acronym) + self.assertEquals(charter.get_state_slug(), slug) + self.assertEquals(charter.docevent_set.count(), events_before + 1) + self.assertTrue("State changed" in charter.docevent_set.all()[0].desc) + if slug == "extrev": + self.assertEquals(len(outbox), mailbox_before + 2) + self.assertTrue("State changed" in outbox[-1]['Subject']) + self.assertTrue("State changed" in outbox[-2]['Subject']) + else: + self.assertEquals(len(outbox), mailbox_before + 1) + if slug == "approved": + self.assertTrue("Charter approved" in outbox[-1]['Subject']) + else: + self.assertTrue("State changed" in outbox[-1]['Subject']) + + def test_conclude(self): + make_test_data() + + # And make a charter for group + group = Group.objects.get(acronym="mars") + + # -- Test conclude WG -- + url = urlreverse('wg_conclude', kwargs=dict(name=group.acronym)) + 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 textarea[name=instructions]')), 1) + + # faulty post + r = self.client.post(url, dict(instructions="")) # No instructions + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + + # conclusion request + r = self.client.post(url, dict(instructions="Test instructions")) + self.assertEquals(r.status_code, 302) + # The WG remains active until the state is set to conclude via change_state + group = Group.objects.get(acronym=group.acronym) + self.assertEquals(group.state_id, "active") + +class WgInfoTestCase(django.test.TestCase): + fixtures = ['names'] + + def setUp(self): + self.charter_dir = os.path.abspath("tmp-charter-dir") + os.mkdir(self.charter_dir) + settings.CHARTER_PATH = self.charter_dir + + def tearDown(self): + shutil.rmtree(self.charter_dir) + + def test_create(self): + make_test_data() + + # -- Test WG creation -- + url = urlreverse('wg_create') + login_testing_unauthorized(self, "secretary", url) + + num_wgs = len(Group.objects.filter(type="wg")) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form input[name=acronym]')), 1) + + # faulty post + r = self.client.post(url, dict(acronym="foobarbaz")) # No name + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + self.assertEquals(len(Group.objects.filter(type="wg")), num_wgs) + + # creation + r = self.client.post(url, dict(acronym="testwg", name="Testing WG")) + self.assertEquals(r.status_code, 302) + self.assertEquals(len(Group.objects.filter(type="wg")), num_wgs + 1) + group = Group.objects.get(acronym="testwg") + self.assertEquals(group.name, "Testing WG") + # check that a charter was created with the correct name + self.assertEquals(group.charter.name, "charter-ietf-testwg") + # and that it has no revision + self.assertEquals(group.charter.rev, "") + + + def test_edit_info(self): + make_test_data() + + # And make a charter for group + group = Group.objects.get(acronym="mars") + + url = urlreverse('wg_edit_info', kwargs=dict(name=group.acronym)) + 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=parent]')), 1) + self.assertEquals(len(q('form input[name=acronym]')), 1) + + # faulty post + Group.objects.create(name="Collision Test Group", acronym="collide") + r = self.client.post(url, dict(acronym="collide")) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + + # Create old acronym + group.acronym = "oldmars" + group.save() + save_group_in_history(group) + group.acronym = "mars" + group.save() + + # post with warning + r = self.client.post(url, dict(acronym="oldmars")) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + + # edit info + area = group.parent + ad = Person.objects.get(name="Aread Irector") + r = self.client.post(url, + dict(name="Mars Not Special Interest Group", + acronym="mnsig", + parent=area.pk, + ad=ad.pk, + chairs="aread@ietf.org, ad1@ietf.org", + secretaries="aread@ietf.org, ad1@ietf.org, ad2@ietf.org", + techadv="aread@ietf.org", + list_email="mars@mail", + list_subscribe="subscribe.mars", + list_archive="archive.mars", + urls="http://mars.mars (MARS site)" + )) + self.assertEquals(r.status_code, 302) + + group = Group.objects.get(acronym="mnsig") + self.assertEquals(group.name, "Mars Not Special Interest Group") + self.assertEquals(group.parent, area) + 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.assertEquals(group.list_email, "mars@mail") + self.assertEquals(group.list_subscribe, "subscribe.mars") + self.assertEquals(group.list_archive, "archive.mars") + self.assertEquals(group.groupurl_set.all()[0].url, "http://mars.mars") + self.assertEquals(group.groupurl_set.all()[0].name, "MARS site") + + def test_edit_telechat_date(self): + make_test_data() + + # And make a charter for group + group = Group.objects.get(acronym="mars") + charter = group.charter + + url = urlreverse('wg_edit_info', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + # add to telechat + self.assertTrue(not charter.latest_event(TelechatDocEvent, "scheduled_for_telechat")) + telechat_date = TelechatDate.objects.active()[0].date + r = self.client.post(url, dict(name=group.name, acronym=group.acronym, telechat_date=telechat_date.isoformat())) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name=charter.name) + self.assertTrue(charter.latest_event(TelechatDocEvent, "scheduled_for_telechat")) + self.assertEquals(charter.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, telechat_date) + + # change telechat + telechat_date = TelechatDate.objects.active()[1].date + r = self.client.post(url, dict(name=group.name, acronym=group.acronym, telechat_date=telechat_date.isoformat())) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name=charter.name) + self.assertEquals(charter.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, telechat_date) + + # remove from agenda + telechat_date = "" + r = self.client.post(url, dict(name=group.name, acronym=group.acronym, telechat_date=telechat_date)) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name=charter.name) + self.assertTrue(not charter.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date) + + def test_submit_charter(self): + make_test_data() + + # And make a charter for group + group = Group.objects.get(acronym="mars") + charter = group.charter + + url = urlreverse('wg_submit', kwargs=dict(name=group.acronym)) + 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=txt]')), 1) + + prev_rev = charter.rev + + test_file = StringIO("hello world") + test_file.name = "unnamed" + + r = self.client.post(url, dict(txt=test_file)) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name="charter-ietf-%s" % group.acronym) + self.assertEquals(charter.rev, next_revision(prev_rev)) + self.assertTrue("new_revision" in charter.latest_event().type) + +class WgAddCommentTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_add_comment(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + url = urlreverse('wg_add_comment', kwargs=dict(name=group.acronym)) + 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 textarea[name=comment]')), 1) + + # request resurrect + comments_before = group.groupevent_set.filter(type="added_comment").count() + + r = self.client.post(url, dict(comment="This is a test.")) + self.assertEquals(r.status_code, 302) + + self.assertEquals(group.groupevent_set.filter(type="added_comment").count(), comments_before + 1) + self.assertTrue("This is a test." in group.groupevent_set.filter(type="added_comment").order_by('-time')[0].desc) + +class WgEditPositionTestCase(django.test.TestCase): + fixtures = ['names', 'ballot'] + + def test_edit_position(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + charter = group.charter + + url = urlreverse('wg_edit_position', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "ad", url) + + p = Person.objects.get(name="Aread Irector") + + e = DocEvent() + e.type = "started_iesg_process" + e.by = p + e.doc = charter + e.desc = "IESG process started" + e.save() + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + charter.save() + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form input[name=position]')) > 0) + self.assertEquals(len(q('form textarea[name=comment]')), 1) + + # vote + pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() + self.assertTrue(not charter.docevent_set.filter(type="changed_ballot_position", by__name="Aread Irector")) + + r = self.client.post(url, dict(position="block", + block_comment="This is a blocking test.", + comment="This is a test.")) + self.assertEquals(r.status_code, 302) + + self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) + e = charter.latest_event(GroupBallotPositionDocEvent) + self.assertTrue("This is a blocking test." in e.block_comment) + self.assertTrue("This is a test." in e.comment) + self.assertTrue(e.pos_id, "block") + + # recast vote + pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() + + r = self.client.post(url, dict(position="yes")) + self.assertEquals(r.status_code, 302) + + self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) + e = charter.latest_event(GroupBallotPositionDocEvent) + self.assertTrue(e.pos_id, "yes") + + # clear vote + pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() + + r = self.client.post(url, dict(position="norecord")) + self.assertEquals(r.status_code, 302) + + self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) + e = charter.latest_event(GroupBallotPositionDocEvent) + self.assertTrue(e.pos_id, "norecord") + + def test_edit_position_as_secretary(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + charter = group.charter + + url = urlreverse('wg_edit_position', kwargs=dict(name=group.acronym)) + p = Person.objects.get(name="Aread Irector") + url += "?ad=%d" % p.id + login_testing_unauthorized(self, "secretary", url) + + e = DocEvent() + e.type = "started_iesg_process" + e.by = p + e.doc = charter + e.desc = "IESG process started" + e.save() + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + charter.save() + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form input[name=position]')) > 0) + + # vote for rhousley + pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() + self.assertTrue(not charter.docevent_set.filter(type="changed_ballot_position", by__name="Sec Retary")) + + r = self.client.post(url, dict(position="no")) + self.assertEquals(r.status_code, 302) + + self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) + e = charter.latest_event(GroupBallotPositionDocEvent) + self.assertTrue(e.pos_id, "no") + + def test_send_ballot_comment(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + charter = group.charter + + url = urlreverse('wg_send_ballot_comment', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "ad", url) + + p = Person.objects.get(name="Aread Irector") + + e = DocEvent() + e.type = "started_iesg_process" + e.by = p + e.doc = charter + e.desc = "IESG process started" + e.save() + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + charter.save() + + GroupBallotPositionDocEvent.objects.create( + doc=charter, + by=p, + type="changed_ballot_position", + pos=GroupBallotPositionName.objects.get(slug="block"), + ad=p, + block_comment="This is a block test", + comment="This is a test", + ) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form input[name="cc"]')) > 0) + + # send + p = Person.objects.get(name="Aread Irector") + mailbox_before = len(outbox) + + r = self.client.post(url, dict(cc="test@example.com", cc_state_change="1")) + self.assertEquals(r.status_code, 302) + + self.assertEquals(len(outbox), mailbox_before + 1) + self.assertTrue("BLOCKING COMMENT" in outbox[-1]['Subject']) + self.assertTrue("COMMENT" in outbox[-1]['Subject']) + +class WgApproveBallotTestCase(django.test.TestCase): + fixtures = ['names'] + + def setUp(self): + self.charter_dir = os.path.abspath("tmp-charter-dir") + os.mkdir(self.charter_dir) + settings.CHARTER_PATH = self.charter_dir + + def tearDown(self): + shutil.rmtree(self.charter_dir) + + def test_approve_ballot(self): + make_test_data() + + group = Group.objects.get(acronym="ames") + charter = group.charter + + url = urlreverse('wg_approve_ballot', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + with open(os.path.join(self.charter_dir, "charter-ietf-%s-%s.txt" % (group.acronym, charter.rev)), "w") as f: + f.write("This is a charter.") + + p = Person.objects.get(name="Aread Irector") + + e = DocEvent() + e.type = "started_iesg_process" + e.by = p + e.doc = charter + e.desc = "IESG process started" + e.save() + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue("Send out the announcement" in q('input[type=submit]')[0].get('value')) + self.assertEquals(len(q('pre')), 1) + + # approve + mailbox_before = len(outbox) + + r = self.client.post(url, dict()) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name=charter.name) + self.assertEquals(charter.get_state_slug(), "approved") + + self.assertEquals(charter.rev, "01") + self.assertTrue(os.path.exists(os.path.join(self.charter_dir, "charter-ietf-%s-%s.txt" % (group.acronym, charter.rev)))) + + self.assertEquals(len(outbox), mailbox_before + 2) + self.assertTrue("WG Action" in outbox[-1]['Subject']) + self.assertTrue("Charter approved" in outbox[-2]['Subject']) diff --git a/ietf/wgcharter/testurl.list b/ietf/wgcharter/testurl.list new file mode 100644 index 000000000..fe9e47c8f --- /dev/null +++ b/ietf/wgcharter/testurl.list @@ -0,0 +1,55 @@ +200 / +200 /wgcharter/ + +# In IESG process +200 /wgcharter/in_process/ + +# by AD +# current AD -- needs to be updated at some point +200 /wgcharter/ad/aread.irector/ +# former AD +200 /wgcharter/ad/exaread.irector/ +404 /wgcharter/ad/no.body/ + +# create +302 /wgcharter/create/ + +# WG +200 /wgcharter/mars/ +200 /wgcharter/mars/00-00/ +200 /wgcharter/mars/_ballot.data + +# Edit WG +302 /wgcharter/mars/edit/state/ +302 /wgcharter/mars/edit/info/ +302 /wgcharter/mars/edit/conclude/ +302 /wgcharter/mars/edit/addcomment/ +302 /wgcharter/mars/edit/action/ +302 /wgcharter/mars/edit/review/ + +# ballots +302 /wgcharter/mars/edit/position/ +302 /wgcharter/mars/edit/sendballotcomment/ +302 /wgcharter/mars/edit/approveballot/ + +# submission of charters +302 /wgcharter/mars/submit/ + +# search +200 /wgcharter/search/ +302 /wgcharter/search/?name=martian +200 /wgcharter/search/?name=something +200 /wgcharter/search/?name=something&by=acronym&acronym=some +200 /wgcharter/search/?name=something&by=state&state=active&charter_state= +200 /wgcharter/search/?name=something&by=state&state=&charter_state=approved +200 /wgcharter/search/?name=something&by=ad&ad=1 +200 /wgcharter/search/?name=something&by=area&area=2 +200 /wgcharter/search/?name=something&by=anyfield&anyfield=something +200 /wgcharter/search/?name=something&by=eacronym&eacronym=someold + +# searchPerson (ajax) +200 /wgcharter/searchPerson/ + +# + + diff --git a/ietf/wgcharter/urls.py b/ietf/wgcharter/urls.py new file mode 100644 index 000000000..999f454b8 --- /dev/null +++ b/ietf/wgcharter/urls.py @@ -0,0 +1,31 @@ +# Copyright The IETF Trust 2011, All Rights Reserved + +from django.conf.urls.defaults import patterns, url +from ietf.wgcharter import views, views_search, views_edit, views_ballot, views_submit +from redesign.doc.models import State + +urlpatterns = patterns('django.views.generic.simple', + url(r'^help/state/$', 'direct_to_template', { 'template': 'wgcharter/states.html', 'extra_context': { 'states': State.objects.filter(type="charter") } }, name='help_charter_states'), +) +urlpatterns += patterns('', + (r'^/?$', views_search.search_main), + url(r'^create/$', views_edit.edit_info, name="wg_create"), + url(r'^search/$', views_search.search_results, name="wg_search"), + (r'^searchPerson/$', views_search.search_person), + url(r'^area/(?P[A-Za-z0-9.-]+)/$', views_search.by_area, name="wg_search_by_area"), + url(r'^in_process/$', views_search.in_process, name="wg_search_in_process"), + url(r'^(?P[A-Za-z0-9._+-]+)/((?P[0-9][0-9](-[0-9][0-9])?)/)?((?Pballot|writeup|history)/)?$', views.wg_main, name="wg_view"), + (r'^(?P[A-Za-z0-9._+-]+)/_ballot.data$', views.wg_ballot), + url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/$', views_edit.change_state, name='wg_change_state'), + url(r'^(?P[A-Za-z0-9._+-]+)/edit/(?P