Final version of charter tool in a new branch

- Legacy-Id: 3341
This commit is contained in:
Martin Qvist 2011-08-19 03:44:06 +00:00
parent f87a378d5f
commit 0a34a909c0
76 changed files with 4452 additions and 92 deletions

View file

@ -189,7 +189,6 @@ class EditInfoTestCase(django.test.TestCase):
status_date=str(date.today() + timedelta(2)),
via_rfc_editor="1",
ad=str(new_ad.pk),
create_in_state="pub-req",
notify="test@example.com",
note="New note",
telechat_date="",
@ -214,7 +213,6 @@ class EditInfoTestCase(django.test.TestCase):
data = dict(intended_std_level=str(draft.intended_std_level_id),
status_date=str(date.today() + timedelta(2)),
via_rfc_editor="1",
create_in_state="pub-req",
ad=str(draft.ad_id),
notify="test@example.com",
note="",
@ -275,7 +273,6 @@ class EditInfoTestCase(django.test.TestCase):
status_date=str(date.today() + timedelta(2)),
via_rfc_editor="1",
ad=ad.pk,
create_in_state="watching",
notify="test@example.com",
note="This is a note",
telechat_date="",
@ -284,7 +281,6 @@ class EditInfoTestCase(django.test.TestCase):
draft = Document.objects.get(name=draft.name)
self.assertTrue(draft.tags.filter(slug="via-rfc"))
self.assertEquals(draft.iesg_state_id, "watching")
self.assertEquals(draft.ad, ad)
self.assertEquals(draft.note, "This is a note")
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat"))

View file

@ -183,9 +183,6 @@ class InternetDraft(models.Model):
return "<%s>" % (self.filename_with_rev())
def filename_with_rev(self):
return "%s-%s.txt" % (self.filename, self.revision_display())
def name(self):
# small hack to make model forward-compatible with new schema
return self.filename
def group_acronym(self):
return self.group.acronym
def group_ml_archive(self):
@ -258,11 +255,11 @@ class PersonOrOrgInfo(models.Model):
date_created = models.DateField(auto_now_add=True, null=True)
created_by = models.CharField(blank=True, null=True, max_length=8)
address_type = models.CharField(blank=True, null=True, max_length=4)
def save(self, **kwargs):
def save(self):
self.first_name_key = self.first_name.upper()
self.middle_initial_key = self.middle_initial.upper()
self.last_name_key = self.last_name.upper()
super(PersonOrOrgInfo, self).save(**kwargs)
super(PersonOrOrgInfo, self).save()
def __str__(self):
# For django.VERSION 0.96
if self.first_name == '' and self.last_name == '':
@ -276,12 +273,16 @@ class PersonOrOrgInfo(models.Model):
def email(self, priority=1, type=None):
name = unicode(self)
email = ''
addresses = self.emailaddress_set.filter(address__contains="@").order_by('priority')
if addresses:
email = addresses[0].address
for a in addresses:
if a.priority == priority:
email = a.address
types = type and [ type ] or [ "INET", "Prim", None ]
for type in types:
try:
if type:
email = self.emailaddress_set.get(priority=priority, type=type).address
else:
email = self.emailaddress_set.get(priority=priority).address
break
except (EmailAddress.DoesNotExist, AssertionError):
pass
return (name, email)
# Added by Sunny Lee to display person's affiliation - 5/26/2007
def affiliation(self, priority=1):
@ -409,9 +410,6 @@ class Rfc(models.Model):
return "%s.txt" % ( self.filename() )
def filename(self):
return "rfc%d" % ( self.rfc_number )
def name(self):
# small hack to make model forward-compatible with new schema
return self.filename()
def revision(self):
return "RFC"
def revision_display(self):
@ -1139,23 +1137,6 @@ class DocumentWrapper(object):
def __init__(self, document):
self.document = document
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
InternetDraftOld = InternetDraft
IDInternalOld = IDInternal
RfcOld = Rfc
BallotInfoOld = BallotInfo
IDStateOld = IDState
IDSubStateOld = IDSubState
AreaOld = Area
AcronymOld = Acronym
IESGLoginOld = IESGLogin
IETFWGOld = IETFWG
IRTFOld = IRTF
from redesign.doc.proxy import InternetDraft, IDInternal, BallotInfo, Rfc
from redesign.name.proxy import IDState, IDSubState
from redesign.group.proxy import Area, Acronym, IETFWG, IRTF
from redesign.person.proxy import IESGLogin
# changes done by convert-096.py:changed maxlength to max_length
# removed core

View file

@ -145,6 +145,7 @@ INSTALLED_APPS = (
'ietf.submit',
'ietf.ietfworkflows',
'ietf.wgchairs',
'ietf.wgrecord',
)
INTERNAL_IPS = (

View file

@ -40,12 +40,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<li><a href="{% url doc_search_by_ad name=user.get_profile.person.full_name_as_key %}">My Documents</a></li>
<li><a href="{% url ietf.iesg.views.agenda_documents %}">Next Telechat</a></li>
<li><a href="{% url ietf.iesg.views.discusses %}">Discusses</a></li>
<li><a href="{% url iesg_working_group_actions %}">Working Groups</a></li>
<li><a href="{% url wg_search_by_ad name=user.get_profile.person.full_name_as_key %}">Working Groups</a></li>
{% endif %}
{% if user|in_group:"Secretariat" %}
<li class="sect first">Secretariat</li>
<li><a href="{% url ietf.iesg.views.telechat_dates %}">Telechat Dates</a></li>
<li><a href="{% url iesg_working_group_actions %}">Working Groups</a></li>
<li><a href="{% url wg_search_in_process %}">Working Groups</a></li>
{% endif %}
<li class="sect{% if not user|in_group:"Area_Director,Secretariat" %} first{% endif %}">Working Groups</li>
@ -74,6 +74,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<li><a href="http://www.ietf.org/meeting/upcoming.html">Upcoming</a></li>
<li class="sect">Other Documents</li>
<li><a href="/wgrecord/">WG Records</a></li>
<li><a href="/ipr/">IPR Disclosures</a></li>
<li><a href="/liaison/">Liaison&nbsp;Statements</a></li>
<li><a href="/iesg/agenda/">IESG Agenda</a></li>

View file

@ -0,0 +1,28 @@
{# Copyright The IETF Trust 2011, All Rights Reserved #}
{% load ietf_filters %}
{{ obj.info.text|safe }}<br/>
<br/>
{% with obj.group as wg %}
WG name: {{ wg.name }}<br/>
WG acronym: {{ wg.acronym }}<br/>
IETF area: {{ wg.parent|default:"-" }}<br/>
WG chairs: {% for n in obj.chairs %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}<br/>
WG secretaries: {% for n in obj.secr %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}<br/>
WG technical advisors: {% for n in obj.techadv %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}<br/>
Assigned AD: {{ wg.ad }}<br/>
Mailing list: {{ wg.list_email }}<br/>
Mailing list subscribe {{ wg.list_subscribe }}<br/>
Mailing list archive: {{ wg.list_archive }}<br/>
Other web sites: {% for a in wg.groupurl_set.all %}{{ a.url }} {% if a.name %}({{ a.name }}){% endif %}{% if not forloop.last %}, {% endif %}{% endfor %}<br/>
WG State: {{ wg.state|safe }}<br/>
Charter State: {{ wg.charter.charter_state|safe }}<br/>
<br/>
{% if obj.rev %}
{{ obj.charter|safe }}
{% else %}
The WG does not yet have a charter
{% endif %}
{% endwith %}

View file

@ -0,0 +1 @@
{% load ietf_filters %}WG Record for {{obj.group.name}} ({{ obj.group.acronym|safe }})

View file

@ -3,7 +3,7 @@
{% block title %}Edit info on {{ doc }}{% endblock %}
{% block morecss %}
form.edit-info #id_notify {
form.edit-info #id_state_change_notice_to {
width: 600px;
}
form.edit-info #id_note {

View file

@ -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 %}
<div {% if concluded %}class="ietf-concluded-bg"{% endif %}>
<h1>{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}){% if concluded %}<br/><span class="ietf-concluded-warning">(concluded WG)</span>{% endif %}</h1>
<div {% if concluded %}class="ietf-concluded-bg"{% endif %} {% if proposed %}class="ietf-proposed-bg"{% endif %}>
<h1>{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}){% if concluded %}<br/><span class="ietf-concluded-warning">(concluded WG)</span>{% endif %}{% if proposed %}<br/><span class="ietf-proposed-warning">(proposed WG)</span>{% endif %}</h1>
<div class="ietf-navset">
{% ifequal selected "documents" %}<span class="selected">Documents</span>{% else %}<a href="/wg/{{wg}}/">Documents</a>{% endifequal %} |

View file

@ -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 %}
<h1>Add comment on {{ wg.acronym }}</h1>
<p>The comment will be added to the history trail.</p>
<form class="add-comment" action="" method="POST">
<table>
{{ form.as_table }}
<tr>
<td></td>
<td class="actions">
<a href="{% url record_view name=wg.acronym %}">Back</a>
<input type="submit" value="Add comment"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -0,0 +1,33 @@
{% extends "base.html" %}
{% block title %}WG {{ announcement }} announcement writeup for {{ doc.group.acronym }}{% endblock %}
{% block morecss %}
form #id_announcement_text {
width: 700px;
height: 600px;
}
{% endblock %}
{% block content %}
<h1>WG {{ announcement }} announcement writeup for {{ doc.group.acronym }}</h1>
<form action="" method="POST">
{{ announcement_text_form.announcement_text }}
<div class="actions">
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Save WG {{ announcement }} announcement text" />
</div>
</form>
{% load ietf_filters %}
{% if user|in_group:"Secretariat" %}
<p>
{% if can_announce %}
<a href="{% url doc_approve_ballot name=doc.name %}">Approve ballot</a>
{% endif %}
</p>
{% endif %}
{% endblock%}

View file

@ -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 %}
<h1>Approve Ballot for {{ wg.acronym }}</h1>
<div>IETF announcement:</div>
<form class="approve-ballot" action="" method="POST">
<div class="announcement">
<pre>{{ announcement }}</pre>
</div>
<div class="actions">
<a href="{% url record_view name=wg.acronym %}">Back</a>
<input type="submit" value="Send out the announcement, close ballot and update revision"/>
</div>
</form>
{% endblock %}

View file

@ -0,0 +1,25 @@
{% autoescape off %}{{ ad }} has entered the following ballot position for
{{ doc.filename }}-{{ doc.revision_display }}: {{ 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 %}

View file

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block title %}Ballot for {{ doc }} issued{% endblock %}
{% block content %}
<h1>Ballot for {{ doc }} issued</h1>
<p>Ballot has been sent out.</p>
<div class="actions">
<a href="{{ back_url }}">Back to document</a>
</div>
{% endblock %}

View file

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block title %}Ballot writeup and notes for {{ doc }}{% endblock %}
{% block morecss %}
form #id_ballot_writeup {
width: 700px;
height: 600px;
}
{% endblock %}
{% block content %}
<h1>Ballot writeup and notes for {{ doc }}</h1>
<form action="" method="POST">
<p>(Technical Summary, Working Group Summary, Document Quality,
Personnel, RFC Editor Note, IRTF Note, IESG Note, IANA Note)</p>
<p>This text will be appended to all announcements and messages to
the IRTF or RFC Editor.</p>
{{ ballot_writeup_form.ballot_writeup }}
{% if not approval %}<p style="font-style:italic">Ballot cannot be issued before <a href="{% url doc_ballot_approvaltext name=doc.name %}">announcement text</a> is added.</p>{% endif %}
<div class="actions">
<a href="{{ back_url }}">Back</a>
<input type="submit" name="save_ballot_writeup" value="Save Ballot Writeup" />
<input style="margin-left: 8px" type="submit" {% if not approval %}disabled="disabled"{% endif %} name="issue_ballot" value="Save and {% if ballot_issued %}Re-{% endif %}Issue Ballot" />
</div>
</form>
{% endblock%}

View file

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% block title %}Working Groups for {{ ad_name }}{% endblock %}
{% block content %}
<h1>Working Groups for {{ ad_name }}</h1>
<p>
<span id="create_wg" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="/wgrecord/create/">Create WG</a></span></span>
</p>
{% if not recs %}
<p><b>No WGs match your query.</b></p>
{% else %}
<table class="ietf-table ietf-doctable">
<tr>
{% for hdr in meta.hdrs %}
{% include "wgrecord/table_header.html" %}
{% endfor %}
</tr>
{% for wg in recs %}
{% include "wgrecord/search_result_row.html" %}
{% endfor %}
</table>
{% endif %}
{% endblock content %}

View file

@ -0,0 +1,77 @@
{% extends "base.html" %}
{% block title %}Change state of WG {{ wg.acronym }}{% 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 %}
<h1>Change state of {{ wg.acronym }}</h1>
<p class="helptext">For help on the states, see the <a href="{% url help_charter_states %}">state table</a>.</p>
<form class="change-state" action="" method="post">
<table>
{% for field in form.visible_fields %}
<tr>
<th>{{ field.label_tag }}:</th>
<td>{{ field }}
{% ifequal field.name "charter_state" %}
{% ifequal field.errors "warning" %}
<ul><li>The initial review time hasn't elapsed. Proceed anyway? <label><input type="checkbox" name="confirm_state" /></label></li></ul>
{% endifequal %}
{% endifequal %}
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
{% ifnotequal field.name "charter_state" %}
{{ field.errors }}
{% endifnotequal %}
</td>
</tr>
{% endfor %}
<tr>
<td colspan="2" class="actions">
<a href="{% url record_view name=wg.acronym %}">Back</a>
<input type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
{% if prev_state %}
<h3>Or revert to previous state</h3>
<div class="prev-state">
<form action="" method="post">
<input type="hidden" name="charter_state" value="{{ prev_charter_state.slug }}" />
<input type="hidden" name="state" value="{{ prev_state.slug }}" />
<input type="submit" value="Back to {{ prev_charter_state.name }}" />
</form>
</div>
{% endif %}
{% endblock %}
{% block content_end %}
<script type="text/javascript">
var message = {
"infrev": "The WG {{ wg.name }} ({{ wg.acronym }}) has been set to Informal IESG review by {{ login.name }}",
"intrev": "The WG {{ wg.name }} ({{ wg.acronym }}) has been set to Internal review by {{ login.name }}. Please place it on the next IESG telechat and inform the IAB.",
"extrev": "The WG {{ wg.name }} ({{ wg.acronym }}) has been set to External review by {{ login.name }}. Please send out the external review announcement to the appropriate lists.\n\nSend the announcement to other SDOs: Yes\nAdditional recipients of the announcement: ",
"approved": "The WG {{ wg.name }} ({{ wg.acronym }}) has been approved. Please publish the charter and sent the appropriate announcements."
};
</script>
<script type="text/javascript" src="/js/rec-change-state.js"></script>
{% endblock %}

View file

@ -0,0 +1,34 @@
<div class="ietf-box diffTool">
<h2 style="margin-top:0;margin-bottom:4px;">Diffs</h2>
<form action="http{% if request.is_secure %}s{% endif %}://tools.ietf.org/rfcdiff" method="get" target="_blank" style="margin:0;">
<table>
<tr><td>
<label>From:</label> <select name="url1">
{% for c in versions %}
<option value="{{ charter_text_url }}{{c.name}}-{{c.rev}}.txt" {% ifequal forloop.counter 2 %} selected="selected" {% endifequal %}>{{c.name}}-{{c.rev}} ({{c.date}})</option>
{% endfor %}
</select>
</td>
<td rowspan="2" valign="top">
Format:
<select name="difftype">
<option value="--html" selected="selected">Side-by-side</option>
<option value="--abdiff">Before-after</option>
<option value="--chbars">Change bars</option>
<option value="--hwdiff">Wdiff</option>
</select> <input name="submit" value="Go!" type="submit" />
</td>
</tr>
<tr>
<td>
<label>To:</label>
<select name="url2">
{% for c in versions %}
<option value="{{ charter_text_url }}{{c.name}}-{{c.rev}}.txt" {% ifequal forloop.counter 1 %} selected="selected" {% endifequal %}>{{c.name}}-{{c.rev}} ({{c.date}})</option>
{% endfor %}
</select>
</td>
</tr>
</table>
</form>
</div>

View file

@ -0,0 +1,38 @@
{% extends "base.html" %}
{% block title %}Request closing of WG {{ wg.acronym }}{% endblock %}
{% block morecss %}
#id_comment {
width: 40em;
}
form.conclude .actions {
text-align: right;
padding-top: 10px;
}
{% endblock %}
{% block content %}
<h1>Request closing of {{ wg.acronym }}</h1>
<p>
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).
</p>
<form class="conclude" action="" method="post">
<table>
{{ form.as_table }}
<tr>
<td colspan="2" class="actions">
<a href="{% url record_view name=wg.acronym %}">Back</a>
<input type="submit" value="Send request"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -0,0 +1,5 @@
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}<td class="date">{{ wg.time|date:"Y-m-d" }}
</td>

View file

@ -0,0 +1,110 @@
{% extends "base.html" %}
{% block title %}
{% if wg %}
Edit info on {{ wg.acronym }}
{% else %}
Create WG record
{% 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 %}
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
{% endblock %}
{% block content %}
{% load ietf_filters %}
<h1>{% if wg %}
Edit info on {{ wg.acronym }}
{% else %}
Create WG record
{% endif %}
</h1>
<form class="edit-info" action="" method="POST">
<table>
{% for field in form.visible_fields %}
<tr>
<th>{{ field.label_tag }}:</th>
<td>{{ field }}
{% ifequal field.name "acronym" %}
{% ifequal field.errors "error" %}
<ul><li>Acronym already in use.</label></li></ul>
{% endifequal %}
{% ifequal field.errors "warning" %}
<ul><li>Acronym previously used. Use anyway? <label><input type="checkbox" name="confirm_acronym" /></label></li></ul>
{% endifequal %}
{% endifequal %}
{% ifequal field.name "ad" %}
{% if user|in_group:"Area_Director" %}
<label><input type="checkbox" name="ad" value="{{ login.pk }}" /> Assign to me</label>
{% endif %}
{% endifequal %}
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
{% ifnotequal field.name "acronym" %}
{{ field.errors }}
{% endifnotequal %}
</td>
</tr>
{% endfor %}
<tr>
<td></td>
<td class="actions">
{% if wg %}
<a href="{% url record_view name=wg.acronym %}">Back</a>
{% endif %}
<input type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
{% 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("/wgrecord/searchPerson/", { hintText: "",
preventDuplicates: true,
prePopulate: chairs });
$("#id_secretaries").tokenInput("/wgrecord/searchPerson/", { hintText: "",
preventDuplicates: true,
prePopulate: secretaries });
$("#id_techadv").tokenInput("/wgrecord/searchPerson/", { hintText: "",
preventDuplicates: true,
prePopulate: techadv });
$("#id_name").focus();
});
{% endblock %}
{% block content_end %}
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
{% endblock %}

View file

@ -0,0 +1,75 @@
{% extends "base.html" %}
{% block title %}Change position for {{ ad.name }} on {{ wg.acronym }}{% endblock %}
{% block morecss %}
div.ballot-deferred {
margin-top: 8px;
margin-bottom: 8px;
}
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 %}
<h1>Change position for {{ ad.name }} on {{ wg.acronym }}</h1>
<form class="position-form" action="" method="POST">
<div>
<span class="position">{{ form.position }}</span>
<span class="actions">
<input type="submit" name="send_mail" value="Save and send email"/>
<input type="submit" value="Save"/>
</span>
</div>
<div style="clear:left"></div>
<div class="block_comment-widgets" {% ifnotequal form.position.initial "block" %}style="display:none"{% endifnotequal %}>
<div class="block_comment">
{{ form.block_comment.label_tag }}:
{% if old_pos and old_pos.block_comment_time %}<span class="last-edited">(last edited {{ old_pos.block_comment_time }})</span>{% endif %}
</div>
{{ form.block_comment.errors }}
{{ form.block_comment }}
</div>
<div class="comment">
{{ form.comment.label_tag }}:
{% if old_pos and old_pos.comment_time %}<span class="last-edited">(last edited {{ old_pos.comment_time }}){% endif %}</span>
</div>
{{ form.comment }}
<div class="actions">
<a href="{{ return_to_url }}">Back</a>
</div>
{{ form.return_to_url }}
</form>
{% endblock %}
{% block content_end %}
<script type="text/javascript" src="/js/rec-edit-position.js"></script>
{% endblock %}

View file

@ -0,0 +1,5 @@
{% autoescape off %}
{{ text }}
WG Record URL: {{ url }}
{% endautoescape %}

View file

@ -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 %}
<h1>Working Groups in IESG process</h1>
<p>
<span id="create_wg" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="/wgrecord/create/">Create WG</a></span></span>
</p>
{% if not recs %}
<p><b>No WGs match your query.</b></p>
{% else %}
<table class="ietf-table ietf-doctable">
<tr>
{% for hdr in meta.hdrs %}
{% include "wgrecord/table_header.html" %}
{% endfor %}
</tr>
{% for wg in recs %}
{% include "wgrecord/search_result_row.html" %}
{% endfor %}
</table>
{% endif %}
{% endblock content %}

View file

@ -0,0 +1,56 @@
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
<table class="ietf-ballot"><tr valign="top"><td class="left">
{% if user|in_group:"Area_Director,Secretariat" %}
{% if user|in_group:"Area_Director" %}
<div style="margin-top:8px; margin-bottom:8px;"><span id="rec_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url rec_edit_position name=wg.acronym %}">Edit position</a></span></span></div>
{% endif %}
{% if user|in_group:"Secretariat" %}
<div style="margin-top:8px; margin-bottom:8px;"><span id="rec_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url rec_approve_ballot name=wg.acronym %}">Approve ballot</a></span></span></div>
{% endif %}
{% endif %}
<p style="margin-top:1em;"><span class="square" style="background:#c00000;"></span><b>Blocking</b><br/>
{% with info.pos_block as positions %}{% include "wgrecord/record_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:#80ff80;"></span><b>Yes</b><br/>
{% with info.pos_yes as positions %}{% include "wgrecord/record_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:#80ff80;"></span><b>No</b><br/>
{% with info.pos_no as positions %}{% include "wgrecord/record_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:#ffff00;"></span><b>Abstain</b><br/>
{% with info.pos_abstain as positions %}{% include "wgrecord/record_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:white;"></span><b>No Record</b><br/>
{% for p in info.pos_no_record %}
<a{% if user|in_group:"Secretariat" %} href="{% url rec_edit_position name=wg.acronym %}?ad={{ p.id }}" title="Click to edit the position of {{ p.name }}"{% endif %}>{{p.name}}</a><br/>
{% empty %}
<i>none</i>
{% endfor %}
</p>
</td>
<td class="right">
<h2 style="margin-top:12px;">Comments</h2>
{% for pos in info.positions %}
{% if pos.comment or pos.block_comment %}
<h2 class="ballot_ad"><a name="{{pos.ad|slugify}}">{% if pos.is_old_ad %}[{%endif%}{{pos.ad|escape}}{% if pos.is_old_ad %}]{%endif%}</a></h2>
{% if pos.block_comment %}
<p><b>Blocking ({{pos.block_comment_time}})</b> <img src="/images/comment.png" width="14" height="12" alt=""/></p>
<pre>{{pos.block_comment|fill:"80"|escape }}</pre>
{% endif %}
{% if pos.comment %}
<p><b>Comment ({{pos.comment_time}})</b> <img src="/images/comment.png" width="14" height="12" alt=""/></p>
<pre>{{pos.comment|fill:"80"|escape }}</pre>
{% endif %}
{% endif %}
{% endfor %}
</td></tr></table>

View file

@ -0,0 +1,7 @@
{% load ietf_filters %}
{% for p in positions %}
{% if p.is_old_ad %}[{%endif%}<a{% if user|in_group:"Secretariat" %} href="{% url rec_edit_position name=wg.acronym %}?ad={{ p.ad_id }}" title="Click to edit the position of {{ p.ad }}"{% endif %}>{{p.ad}}</a>{% if p.is_old_ad %}]{%endif%}{% if p.comment or p.block_comment %}&nbsp;<a href="#{{p.ad|slugify}}"><img src="/images/comment.png" width="14" height="12" alt="*" border="0"/></a>{% endif %}<br/>
{% if p.old_positions %}<span class="was">(was {{p.old_positions|join:", "}})</span><br/>{%endif%}
{% empty %}
<i>none</i>
{% endfor %}

View file

@ -0,0 +1,44 @@
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
<table class="ietf-table">
<tr><th class="comment_date">Date</th><th>Version</th><th>By</th><th>Text</th></tr>
{% for c in history %}
<tr class="{% cycle oddrow,evenrow %}"{% if c.is_com %} id="history-{{c.comment.id }}"{% endif %}>
<td class="comment_date">{{ c.date|date:"Y-m-d" }}</td>
{% if c.is_rev %}
<td>{{ c.info.version }}</td>
<td>(System)</td>
<td>New version available: <a href="{{ c.txt_url }}{{ c.charter.name }}-{{c.charter.rev}}.txt">{{ c.charter.name }}-{{ c.charter.rev }}</a> {% if c.prev_charter %}(<a href="http://tools.ietf.org/rfcdiff?url1={{ c.txt_url }}{{ c.charter.name }}-{{c.charter.rev}}.txt&url2={{ c.txt_url }}{{ c.prev_charter.name }}-{{c.prev_charter.rev}}.txt">diff from -{{ c.prev_charter.rev }}</a>){% endif %}</td>
{% endif %}
{% if c.is_com %}
<td>{{ c.info.version }}</td>
<td>{{ c.info.by|escape }}</td>
<td>{% if c.comment.ballot %}
[Ballot {{ c.comment.get_ballot_display }}]<br />
{% endif %}
{% if c.info.snipped %}
<div id="commentS{{c.comment.id}}">{{ c.info.textSnippet|safe }}</div>
<span class="comment_toggle" onclick="toggleComment({{c.comment.id}})" id="commentT{{c.comment.id}}">[show all]</span>
<div id="commentF{{c.comment.id}}" style="display:none;">
{{ c.info.text|fill:"80"|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
</div>
{% else %}
{% if c.info.dontmolest %}
{{ c.info.text|safe }}
{% else %}
{{ c.info.text|fill:"80"|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
{% endif %}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,74 @@
{% 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 %}
{% if doc.in_ietf_process %}
<link rel="alternate" type="application/atom+xml" href="/feed/comments/{{ wg.name }}/" />
{% endif %}
<meta name="description" content="{% include "wgrecord/wg_description.html" %}" />
{% endblock %}
{% block title %}{% include "wgrecord/wg_title.html" %}{% endblock title %}
{% block content %}
<h1>{% include "wgrecord/wg_title.html" %}
{% ifequal wg.state_id "conclude" %}<br/><span class="ietf-concluded-warning">(concluded WG)</span>{% endifequal %}
{% ifequal wg.state_id "proposed" %}<br/><span class="ietf-proposed-warning">(proposed WG)</span>{% endifequal %}
</h1>
<div id="mytabs" class="yui-navset">
<ul class="yui-nav">
<li{% ifequal tab "charter" %} class="selected"{% endifequal %}><a href="/wgrecord/{{ wg.acronym }}"><em>WG {% if snapshot %}(snapshot){% endif %}</em></a></li>
<li{% if wg.charter %}{% ifequal wg.charter.charter_state_id "iesgrev" %}{% ifequal tab "ballot" %} class="selected"{% endifequal %}{%else%} class="disabled"{%endifequal%}{% else %} class="disabled"{% endif %}{% if snapshot %} class="disabled"{% endif %}><a href="/wgrecord/{{ wg.acronym }}/ballot/"><em>IESG Review</em></a></li>
<li{% if wg.charter %}{% ifequal wg.charter.charter_state_id "iesgrev" %}{% ifequal tab "writeup" %} class="selected"{% endifequal %}{%else%} class="disabled"{%endifequal%}{% else %} class="disabled"{% endif %}{% if snapshot %} class="disabled"{% endif %}><a href="/wgrecord/{{ wg.acronym }}/writeup/"><em>IESG Writeups</em></a></li>
<li{% ifequal tab "history" %} class="selected"{% endifequal %}{% if snapshot %} class="disabled"{% endif %}><a href="/wgrecord/{{ wg.acronym }}/history/"><em>History</em></a></li>
</ul>
<div class="yui-content">
{% block tab_content %}{% endblock %}
</div> <!-- yui-content -->
</div> <!-- mytabs -->
{% 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 %}

View file

@ -0,0 +1,11 @@
{% extends "wgrecord/record_main.html" %}
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
{% block tab_content %}
{% include "wgrecord/record_ballot.html" %}
{% endblock tab_content %}

View file

@ -0,0 +1,49 @@
{% extends "wgrecord/record_main.html" %}
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
{% block tab_content %}
{% block record_revision %}{% endblock %}
<div class="ietf-box metabox">
<table id="metatable" width="100%">
{% block record_metatable %}{% endblock %}
</table>
<div style="padding-top:6px;padding-bottom:6px;padding-left:2px;">
{% block record_metalinks %}{% endblock %}
</div>
{% block record_metabuttons %}
{% if user|in_group:"Area_Director,Secretariat" %}
{% if not snapshot %}
<div style="padding-bottom:2px;">
<span id="wg_edit_state_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url wg_change_state name=wg.acronym %}">Change state</a></span></span>
{% ifequal wg.state_id "active" %}{% ifequal wg.charter.charter_state_id "approved" %}
<span id="wg_conclude_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url wg_conclude name=wg.acronym %}">Conclude WG</a></span></span>
{% endifequal %}{% endifequal %}
{% ifnotequal wg.state_id "conclude" %}
<span id="wg_edit_info_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url wg_edit_info name=wg.acronym %}">Edit</a></span></span>
{% endifnotequal %}
{% ifnotequal wg.state_id "conclude" %}
<span id="wg_edit_info_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url wg_submit name=wg.acronym %}">Submit charter</a></span></span>
{% endifnotequal %}
</div>
{% endif %}{# if not snapshot #}
{% endif %}{# if user in group #}
{% endblock record_metabuttons%}
</div> <!-- metabox -->
<div id="charterText">
{% block charter_text %}{% endblock %}
</div> <!-- charterText -->
{% endblock tab_content %}
{% block content_end %}
{% endblock content_end %}

View file

@ -0,0 +1,81 @@
{% extends "wgrecord/record_tab_base.html" %}
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
{% block record_revision %}
Snapshots: {% if not snapshot %}<strong>{% else %}<a href="{% url record_view name=wg.acronym %}">{% endif %}current{% if not snapshot %}</strong>{% else %}</a>{% endif %} {% for d in versions reversed %}{% ifnotequal d.rev wg.charter.rev %}{% ifequal snapshot d.rev %}<strong>{% else %}<a href="{% url record_view name=wg.acronym %}{{d.rev}}/">{% endifequal %}{{ d.rev }}{% ifequal snapshot d.rev %}</strong>{% else %}</a>{% endifequal %} {% endifnotequal %}{% endfor %}
{% endblock %}
{% block record_metatable %}
<tr><td style="width: 15em">WG name:</td><td>{% if not snapshot %}{{ wg.name }}{% else %} {{ gh.name }} {% endif %}</td></tr>
<tr><td>WG acronym:</td><td>{% 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 %}</td></tr>
<tr><td>IETF area:</td><td>{% if not snapshot %}{{ wg.parent|default:"-" }}{% else %}{{ gh.parent|default:"-" }}{% endif %}</td></tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
<tr><td>WG chairs:</td><td>{% 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 %}</td></tr>
{% if not snapshot %}
<tr><td>WG secretaries:</td><td>{% for n in info.secr %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}</td></tr>
<tr><td>WG technical advisors:</td><td>{% for n in info.techadv %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}</td></tr>
{% endif %}
<tr><td>Assigned AD:</td><td>{% if not snapshot %}{{ wg.ad }}{% else %}{{ gh.ad }}{% endif %}</td></tr>
{% if not snapshot %}
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
<tr><td>Mailing list:</td><td><a href="mailto:{{ wg.list_email }}">{{ wg.list_email }}</a>{% if info.prev_list_email %}<br />(previous lists: {% for a in info.prev_list_email %}{{ a }}{% if not forloop.last %}, {% else %}){% endif %}{% endfor %}{% endif %}</td></tr>
<tr><td>Mailing list subscribe:</td><td><a href="{{ wg.list_subscribe }}">{{ wg.list_subscribe }}</a>{% if info.prev_list_subscribe %}<br />(previous archives: {% for a in info.prev_list_subscribe %}<a href="{{ a }}">{{ a }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}</td></tr>
<tr><td>Mailing list archive:</td><td><a href="{{ wg.list_archive }}">{{ wg.list_archive }}</a>{% if info.prev_list_archive %}<br />(previous archives: {% for a in info.prev_list_archive %}<a href="{{ a }}">{{ a }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}</td></tr>
<tr><td>Other web sites:</td><td>{% for a in wg.groupurl_set.all %}<a href="{{ a.url }}">{{ a.url }}</a> {% if a.name %}({{ a.name }}){% endif %}{% if not forloop.last %}<br />{% endif %}{% endfor %}</td></tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
<tr><td>WG State:</td><td> {{ wg.state|safe }} </td>
{% ifnotequal wg.state_id "conclude" %}<tr><td><a href="/wgrecord/help/state/">Charter State</>:</td><td> {{ wg.charter.charter_state|safe }} {% ifequal wg.state_id "proposed" %}{% ifnotequal wg.charter.charter_state_id "notrev" %}(Initial Chartering){% endifnotequal %}{% else %}{% ifnotequal wg.charter.charter_state_id "approved" %}(Rechartering){% endifnotequal %}{% endifequal %}</td>{% endifnotequal %}
{% ifequal wg.state_id "proposed" %}
{% if wg.comments %}
<tr><td>Reason for chartering:</td><td>{{ wg.comments }}</td></tr>
{% endif %}
{% endifequal %}
{% ifequal wg.state_id "active" %}
{% ifnotequal wg.charter.charter_state_id "approved" %}
{% if wg.comments %}
<tr><td>Reason for rechartering:</td><td>{{ wg.comments }}</td></tr>
{% endif %}
{% endifnotequal %}
{% endifequal %}
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
<tr><td>Last updated:</td><td> {{ info.last_update|date:"Y-m-d"|default:"(data missing)" }}</td></tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
{% endif %}{# if not snapshot #}
{% endblock record_metatable %}
{% block record_metalinks %}
{% if not snapshot %}
<a href="/feed/wgcomments/{{ wg.acronym }}/">Atom feed</a>
{% endif %}
{% endblock %}
{% block charter_text %}
{% ifnotequal charter.rev "" %}
<p>Other versions: <a href="{{ charter_text_url }}{{wg.charter.name}}-{{rev}}.txt">plain text</a></p>
<h3>Charter {{ charter.name }}-{{ rev }}</h3>
<div class="markup_draft">
{{ content|safe }}
</div>
{% else %}
<h3>The WG does not yet have a charter</h3>
{% endifnotequal %}
{% endblock %}{# charter_text #}

View file

@ -0,0 +1,18 @@
{% extends "wgrecord/record_main.html" %}
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
{% block tab_content %}
{% include "wgrecord/charter_diffs.html" %}
<h2 style="margin-top:1em;">WG History</h2>
{% if user|in_group:"Area_Director,Secretariat" %}
<div style="margin-bottom:8px" id="history_actions">
<span id="rec_add_comment_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url rec_add_comment name=wg.acronym %}">Add comment</a></span></span>
</div>
{% endif %}
{% include "wgrecord/record_history.html" %}
{% endblock tab_content %}

View file

@ -0,0 +1,37 @@
{% extends "wgrecord/record_main.html" %}
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
{% block tab_content %}
<h3>WG Review Announcement</h3>
{% if user|in_group:"Area_Director,Secretariat" %}
<div style="background:#E0E0FF">
<p align=right>
<span id="rec_edit_announce_button" class="yui-button yui-link-button"><span class="first-child">
<a href="{% url rec_announcement_text name=wg.acronym ann="review" %}">Edit WG Review Announcement</a>
</span></span>
</p>
{% endif %}
<pre>
{{ info.review_text|escape|urlize }}
</pre>
</div>
<h3>WG Action Announcement</h3>
{% if user|in_group:"Area_Director,Secretariat" %}
<div style="background:#E0E0FF">
<p align=right>
<span id="rec_edit_announce_button" class="yui-button yui-link-button"><span class="first-child">
<a href="{% url rec_announcement_text name=wg.acronym ann="action" %}">Edit WG Action Announcement</a>
</span></span>
</p>
{% endif %}
<pre>
{{ info.action_text|escape|urlize }}
</pre>
</div>
{% endblock tab_content %}

View file

@ -0,0 +1,137 @@
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
<form name="search_form" id="search_form" class="search_form" action="/wgrecord/search/" method="get">
<div class="search_field">
<label>Name:</label> {{ form.name }}
</div>
<span onclick="toggleAdvanced();"><b><img src="/images/{% if meta.advanced %}minus{% else %}plus{% endif %}.png" alt="" id="search_advanced-img" /> Advanced</b></span>
<div id="search_advanced" style="{% if not meta.advanced %}display:none;{%endif%}margin-top:1em;">
Additional search criteria:
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="acronym" {% ifequal meta.by "acronym" %}checked="checked"{% endifequal %} onclick="changeBy();"/> Acronym:</label> {{ form.acronym }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="state" {% ifequal meta.by "state" %}checked="checked"{% endifequal %} onclick="changeBy();"/> State:</label> {{ form.state }} :: {{ form.charter_state }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="ad" {% ifequal meta.by "ad" %}checked="checked"{% endifequal %} onclick="changeBy();"/> Assigned AD:</label> {{ form.ad }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="area" {% ifequal meta.by "area" %}checked="checked"{% endifequal %} onclick="changeBy();"/> Area:</label> {{ form.area }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="anyfield" {% ifequal meta.by "anyfield" %}checked="checked"{% endifequal %} onclick="changeBy();"/> Text in any field:</label> {{ form.anyfield }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="eacronym" {% ifequal meta.by "eacronym" %}checked="checked"{% endifequal %} onclick="changeBy();"/> Earlier acronym:</label> {{ form.eacronym }}
</div>
</div><!-- end of advanced -->
<div style="padding-top:0.5em;">
<span class="first-child">
<button type="submit" name="search_submit" id="id_search_submit">Search</button>
</span>
</div>
</form>
<script type="text/javascript">
//<![CDATA[
// we want to disable our submit button if we have no search text,
// and we have no advanced options selected
function toggleSubmit() {
var button = document.getElementById("id_search_submit");
var by = findCheckedSearchBy();
var value = findSearchByValue(by);
var text = document.getElementById("id_name");
if ((value == "") && (text.value == "")) {
button.disabled = true;
} else {
button.disabled = false;
}
}
// check our button status after every change to text fields
// Internet Explorer uses 'onpropertychange', everyone else 'oninput'
window.oninput = toggleSubmit;
window.onpropertychange = toggleSubmit;
// check our button status after every change to selection pulldowns
window.onchange = toggleSubmit;
function togglePlusMinus(id) {
var el = document.getElementById(id);
var imgEl = document.getElementById(id+"-img");
if (el.style.display == 'none') {
el.style.display = 'block';
imgEl.src = "/images/minus.png";
} else {
el.style.display = 'none';
imgEl.src = "/images/plus.png";
}
}
function findCheckedSearchBy() {
var by='';
var f = document.search_form;
for (var i = 0; i < f.by.length; i++) {
if (f.by[i].checked) {
by = f.by[i].value;
break;
}
}
return by;
}
function findSearchByValue(by) {
if (by == 'acronym') { return document.getElementById("id_acronym").value; }
if (by == 'state') {
// state might be wg state...
state_value = document.getElementById("id_state").value;
if (state_value) { return state_value; }
// ...or charter state
return document.getElementById("id_charter_state").value;
}
if (by == 'ad') { return document.getElementById("id_ad").value; }
if (by == 'area') { return document.getElementById("id_area").value; }
if (by == 'anyfield') { return document.getElementById("id_anyfield").value; }
if (by == 'eacronym') { return document.getElementById("id_eacronym").value; }
return '';
}
function changeBy() {
var by=findCheckedSearchBy();
var f = document.search_form;
f.acronym.disabled=true;
f.state.disabled=true; f.charter_state.disabled=true;
f.ad.disabled=true;
f.area.disabled=true;
f.anyfield.disabled=true;
f.eacronym.disabled=true;
if (by=='acronym') { f.acronym.disabled=false;}
if (by=='state') { f.state.disabled=false; f.charter_state.disabled=false;}
if (by=='ad') { f.ad.disabled=false; }
if (by=='area') { f.area.disabled=false;}
if (by=='anyfield') { f.anyfield.disabled=false;}
if (by=='eacronym') { f.eacronym.disabled=false;}
toggleSubmit();
}
function toggleAdvanced() {
togglePlusMinus("search_advanced");
var f = document.search_form;
for (var i = 0; i < f.by.length; i++) { f.by[i].checked = false; }
changeBy();
}
changeBy();
//]]>
</script>

View file

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% comment %}
Copyright 2011 The IETF Trust. All rights reserved.
{% endcomment %}
{% block title %}Working Group Records{% endblock %}
{% block content %}
<h1>Working Group Records</h1>
<div class="ietf-box search_form_box">
{% include "wgrecord/search_form.html" %}
</div>
<div id="search_results">
{% if meta.searching %}
{% include "wgrecord/search_results.html" %}
{% endif %}
</div>
{% endblock content %}
{% block scripts %}
YAHOO.util.Event.onContentReady("search_submit_button", function () {
var oButton = new YAHOO.widget.Button("search_submit_button", {});
});
{% endblock scripts %}

View file

@ -0,0 +1,13 @@
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters %}
<tr class="{% cycle oddrow,evenrow %}">
<td class="acronym">
<a href="{% url record_view name=wg.acronym %}">{{ wg.acronym|safe }}</a>
</td>
<td class="title">{{ wg.name }}</td>
{% include "wgrecord/date_column.html" %}
{% include "wgrecord/status_columns.html" %}
</tr>

View file

@ -0,0 +1,22 @@
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% if meta.max %}
<p><b>Too many WGs match the query! Returning partial result only.</b></p>
{% endif %}
{% if not recs %}
<p><b>No WGs match your query.</b></p>
{% else %}
<table class="ietf-table ietf-doctable">
<tr>
{% for hdr in meta.hdrs %}
{% include "wgrecord/table_header.html" %}
{% endfor %}
</tr>
{% for wg in recs %}
{% include "wgrecord/search_result_row.html" %}
{% endfor %}
</table>
{% endif %}

View file

@ -0,0 +1,47 @@
{% 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 %}
<h1>Send ballot position email for {{ ad }}</h1>
<form class="send-ballot" action="" method="POST">
<table>
<tr><th>From:</th> <td>{{ frm }}</td></tr>
<tr><th>To:</th> <td>{{ to }}</td></tr>
<tr>
<th>Cc:<br/>
<span class="help">separated<br/> by comma</span></th>
<td><input type="text" name="cc" value="" size="75" /><br/>
{% if doc.idinternal.state_change_notice_to %}
<label>
<input type="checkbox" name="cc_state_change" value="1" checked="yes" />
{{ doc.idinternal.state_change_notice_to }}
</label>
{% endif %}
</td>
</tr>
<tr><th>Subject:</th> <td>{{ subject }}</td></tr>
<tr>
<th>Body:</th>
<td><pre>{{ body|wrap_text }}</pre></td>
</tr>
<tr>
<td></td>
<td class="actions">
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Send"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -0,0 +1,10 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% block content %}
<h1>{{ state.state }}</h1>
{{ state.description|escape }}
<form action=".">
<input type="button" value="Back" onClick="history.go(-1);"/>
</form>
{% endblock%}

View file

@ -0,0 +1,32 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% block title %}Chartering States{% endblock %}
{% block morecss %}
.state_column {
width: 13em;
}
{% endblock %}
{% block content %}
<h1>Chartering States</h1>
<table class="ietf-table">
<tr>
<th class="state_column">State</th>
<th>Description</th>
</tr>
<tr class="{% cycle oddrow,evenrow as cycle1 %}"></tr>
{% for state in states %}
<tr class="{% cycle cycle1 %}">
<td>{{ state.name|escape }}</td>
<td>{{ state.desc|escape }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View file

@ -0,0 +1,17 @@
{% comment %}
Copyright The IETF Trust 2011, All Rights Reserved
{% endcomment %}
{% load ietf_filters ietf_streams %}{% load rec_ballot_icon %}
<td class="status">
{% if wg.charter %}
{{ wg.charter.charter_state|safe }}
{% else %}
(data missing)
{% endif %}
{% if not hide_telechat_date %}{% if doc.telechat_date %}<br/>IESG Telechat: {{ doc.telechat_date }}{% endif %}{% endif %}
{% block extra_status %}{% endblock %}
</td>
<td class="ballot">
{% ifequal wg.charter.charter_state_id "iesgrev" %}{% rec_ballot_icon wg.acronym %}{% endifequal %}
</td>

View file

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block title %}
Charter submission for {{ wg.acronym }}
{% 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; }
.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; }
{% endblock %}
{% block content %}
<h1>Charter submission for {{ wg.acronym }}</h1>
{% if form.cutoff_warning %}
<div class="cutoff-warning">
{{ form.cutoff_warning|safe }}
</div>
{% endif %}
<p>The text will be submitted as <strong>charter-ietf-{{ wg.acronym }}-{{ next_rev }}</strong></p>
<form class="edit-info" action="" enctype="multipart/form-data" method="POST">
<table>
{% for field in form.visible_fields %}
<tr>
<th>{{ field.label_tag }}:</th>
<td>
{{ field }}
{{ field.errors }}
</td>
</tr>
{% endfor %}
<tr>
<td></td>
<td class="actions">
<a href="{% url record_view name=wg.acronym %}">Back</a>
<input type="submit" value="Submit"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -0,0 +1,15 @@
{# Copyright The IETF Trust 2011, All Rights Reserved #}
<th class="{{hdr.htype}}"{% if hdr.colspan %}colspan="{{ hdr.colspan }}" {% endif %}
onclick="location=unescape('{{ meta.rqps }}&sortBy={{hdr.htype}}');"
style="white-space: nowrap;"
>
<span>
<label>{{hdr.htitle}}</label>
{% if hdr.selected %}
<img style="border-style: none;vertical-align:top" src="/images/sort-header-filled.png"/>
{% else %}
<img style="border-style: none;vertical-align:top" src="/images/sort-header-clear.png"/>
{% endif %}
</span>
</th>

View file

@ -0,0 +1 @@
FIXME: description of {{ wg.name }}

View file

@ -0,0 +1 @@
{{ wg.name }} ({{wg.acronym}})

View file

@ -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.wgrecord.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'^wgrecord/', include('ietf.wgrecord.urls')),
(r'^cookies/', include('ietf.cookies.urls')),
(r'^submit/', include('ietf.submit.urls')),
(r'^streams/', include('ietf.ietfworkflows.urls')),

View file

@ -111,7 +111,8 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
def wg_documents(request, acronym):
wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
concluded = (wg.status_id != 1)
concluded = (wg.status_id != 1 and wg.status_id != 4)
proposed = (wg.status_id == 4)
form = SearchForm({'by':'group', 'group':str(wg.group_acronym.acronym),
'rfcs':'on', 'activeDrafts':'on'})
if not form.is_valid():
@ -130,26 +131,28 @@ def wg_documents(request, acronym):
if ( len(parts) >= 3):
if parts[1] != "ietf" and parts[2].startswith(wg.group_acronym.acronym+"-"):
docs_related_pruned.append(d)
return wg, concluded, docs, meta, docs_related_pruned, meta_related
return wg, concluded, proposed, docs, meta, docs_related_pruned, meta_related
def wg_documents_txt(request, acronym):
wg, concluded, docs, meta, docs_related, meta_related = wg_documents(request, acronym)
return HttpResponse(loader.render_to_string('wginfo/wg_documents.txt', {'wg': wg, 'concluded':concluded, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}),mimetype='text/plain; charset=UTF-8')
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 wg_documents_html(request, acronym):
wg, concluded, docs, meta, docs_related, meta_related = wg_documents(request, acronym)
return render_to_response('wginfo/wg_documents.html', {'wg': wg, 'concluded':concluded, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}, RequestContext(request))
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))
def wg_charter(request, acronym):
wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
concluded = (wg.status_id != 1)
concluded = (wg.status_id != 1 and wg.status_id != 4)
proposed = (wg.status_id == 4)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
fill_in_charter_info(wg)
return render_to_response('wginfo/wg_charterREDESIGN.html',
dict(wg=wg,
concluded=concluded,
proposed=proposed,
selected='charter'),
RequestContext(request))
return render_to_response('wginfo/wg_charter.html', {'wg': wg, 'concluded':concluded, 'selected':'charter'}, RequestContext(request))
return render_to_response('wginfo/wg_charter.html', {'wg': wg, 'concluded':concluded, 'proposed': proposed, 'selected':'charter'}, RequestContext(request))

View file

@ -0,0 +1 @@
#

67
ietf/wgrecord/feeds.py Normal file
View file

@ -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.wgrecord.views_rec import _get_history, _get_html
from wgrecord import markup_txt
import datetime
import re, os
class GroupComments(Feed):
feed_type = Atom1Feed
title_template = "feeds/wg_record_title.html"
description_template = "feeds/wg_record_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('record_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'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="chair"))
h['secr'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="secr"))
h['techadv'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="techadv"))
else:
h['chairs'] = map(lambda x: x.email.person.name, obj.role_set.filter(name__slug="chair"))
h['secr'] = map(lambda x: x.email.person.name, obj.role_set.filter(name__slug="secr"))
h['techadv'] = map(lambda x: x.email.person.name, 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(
str(obj.charter.name)+"-"+str(obj.charter.rev)+",html",
os.path.join(obj.charter.get_file_path(), obj.charter.name+"-"+obj.charter.rev+".txt"))
return history
def item_link(self, obj):
return reverse('record_view', kwargs={'name': obj['group'].acronym})
def item_pubdate(self, obj):
return obj['date']

36
ietf/wgrecord/mails.py Normal file
View file

@ -0,0 +1,36 @@
# 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, BallotPositionDocEvent, LastCallDocEvent, DocAlias
from redesign.person.models import Person
# These become part of the subject of the email
types = {}
types['state'] = "State changed"
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]),
"wgrecord/email_secretariat.txt",
dict(text=text,
url=settings.IDTRACKER_BASE_URL + urlreverse('record_view', kwargs=dict(name=wg.acronym))))

View file

@ -0,0 +1,66 @@
# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
#
# 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<span class="m_ftr">\g<1></span>\n<span class="m_hdr">\g<2></span>\n""", content)
content = re.sub("\n(.+\[Page \d+\])\n\s*$", """\n<span class="m_ftr">\g<1></span>\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<span class="m_h">\g<1>\g<2></span>""", content)
return "<pre>"+content+"</pre>\n"

View file

@ -0,0 +1 @@
#

View file

@ -0,0 +1,128 @@
# 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 == []:
pos = GroupBallotPositionDocEvent(doc=doc, ad=p, by=p)
pos.save()
latest_positions.append(pos)
else:
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)+"<!-- adId="+str(adId)+" my="+str(my)+"-->"
def render_ballot_icon2(acronym, red,yellow,green,gray,blank, my, adId):
edit_position_url = urlreverse('rec_edit_position', kwargs=dict(name=acronym))
if adId:
res_cm = ' oncontextmenu="editRecBallot(\''+str(edit_position_url)+'\');return false;"'
else:
res_cm = ''
res = '<table class="ballot_icon" title="IESG Review (click to show more, right-click to edit position)" onclick="showRecBallot(\'' + acronym + '\',\'' + str(edit_position_url) + '\')"'+res_cm+'>'
for y in range(3):
res = res + "<tr>"
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 + '<td class="'+c+' ballot_icon_my" />'
my = None
else:
res = res + '<td class="'+c+'" />'
res = res + '</tr>'
res = res + '</table>'
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('rec_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

22
ietf/wgrecord/tests.py Normal file
View file

@ -0,0 +1,22 @@
# Copyright The IETF Trust 2011, All Rights Reserved
import os
import unittest
from django.conf import settings
from ietf.utils.test_utils import SimpleUrlTestCase
class WgRecUrlTestCase(SimpleUrlTestCase):
def testUrls(self):
self.doTestUrls(__file__)
class WgRecFileTestCase(unittest.TestCase):
def testFileExistence(self):
print " Testing if WG charter texts exist locally"
fpath = os.path.join(settings.CHARTER_PATH, "charter-ietf-example-01.txt")
if not os.path.exists(fpath):
print "\nERROR: exampe charter text not found in "+settings.CHARTER_PATH
print "Needed for testing WG record pages."
print "Remember to set CHARTER_PATH in settings_local.py\n"
else:
print "OK (seem to exist)"

29
ietf/wgrecord/urls.py Normal file
View file

@ -0,0 +1,29 @@
# Copyright The IETF Trust 2011, All Rights Reserved
from django.conf.urls.defaults import patterns, url
from ietf.wgrecord import views_rec, views_search, views_edit, views_ballot, views_submit
from redesign.name.models import CharterDocStateName
urlpatterns = patterns('django.views.generic.simple',
url(r'^help/state/$', 'direct_to_template', { 'template': 'wgrecord/states.html', 'extra_context': { 'states': CharterDocStateName.objects.all() } }, name='help_charter_states'),
)
urlpatterns += patterns('',
(r'^/?$', views_search.search_main),
url(r'^create/$', views_edit.edit_info, name="wg_create"),
(r'^search/$', views_search.search_results),
(r'^searchPerson/$', views_search.search_person),
url(r'^ad/(?P<name>[A-Za-z0-9.-]+)/$', views_search.by_ad, name="wg_search_by_ad"),
url(r'^in_process/$', views_search.in_process, name="wg_search_in_process"),
url(r'^(?P<name>[A-Za-z0-9._-]+)/((?P<rev>[0-9][0-9](-[0-9][0-9])?)/)?((?P<tab>ballot|writeup|history)/)?$', views_rec.record_main, name="record_view"),
(r'^(?P<name>[A-Za-z0-9._-]+)/_ballot.data$', views_rec.record_ballot),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/state/$', views_edit.change_state, name='wg_change_state'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/info/$', views_edit.edit_info, name='wg_edit_info'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/conclude/$', views_edit.conclude, name='wg_conclude'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/addcomment/$', views_edit.add_comment, name='rec_add_comment'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/(?P<ann>action|review)/$', views_ballot.announcement_text, name='rec_announcement_text'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/position/$', views_ballot.edit_position, name='rec_edit_position'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/sendballotcomment/$', views_ballot.send_ballot_comment, name='rec_send_ballot_comment'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='rec_approve_ballot'),
url(r'^(?P<name>[A-Za-z0-9._-]+)/submit/$', views_submit.submit, name='wg_submit'),
)

114
ietf/wgrecord/utils.py Normal file
View file

@ -0,0 +1,114 @@
from django.conf import settings
import re
from redesign.group.models import GroupEvent
from ietf.utils.history import find_history_active_at
def add_wg_comment(request, wg, text, ballot=None):
if request:
login = request.user.get_profile()
else:
login = None
e = GroupEvent(group=wg, type="added_comment", by=login)
e.desc = text
e.save()
def log_state_changed(request, doc, by, prev_state, note=''):
from doc.models import DocEvent
e = DocEvent(doc=doc, by=by)
e.type = "changed_document"
e.desc = u"State changed to <b>%s</b> from %s" % (
doc.charter_state.name,
prev_state.name if prev_state else "None")
if note:
e.desc += "<br>%s" % note
e.save()
return e
def log_info_changed(request, wg, by, type=None, note=''):
from group.models import GroupEvent
e = GroupEvent(group=wg, by=by)
e.type = "changed_record"
e.desc = u"Info changed"
if note:
e.desc += "<br>%s" % note
e.save()
return e
def get_charter_for_revision(charter, r):
if r == None:
return None
else:
l = list(charter.history_set.filter(rev=r).order_by('-time'))
if l != []:
return l[0]
else:
return charter
def get_group_for_revision(wg, r):
if r == None:
return None
else:
l = list(wg.charter.history_set.filter(rev=r).order_by('-time'))
if l != []:
o = list(wg.history_set.filter(time__lte=l[0].time).order_by('-time'))
if o != []:
return o[0]
else:
return wg
else:
return wg
def prev_revision(rev):
m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
if m.group('minor') and m.group('minor') != "00":
return "%s-%#02d" % (m.group('major'), int(m.group('minor')) - 1)
else:
return None
def next_revision(rev):
if rev == "":
return "00-00"
m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
if m.group('minor'):
return "%s-%#02d" % (m.group('major'), int(m.group('minor')) + 1)
else:
return "%s-00" % (m.group('major'))
def next_approved_revision(rev):
if rev == "":
return "01"
m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
return "%#02d" % (int(m.group('major')) + 1)
def update_telechat(request, doc, by, new_telechat_date):
from doc.models import TelechatDocEvent
on_agenda = bool(new_telechat_date)
prev = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
prev_telechat = prev.telechat_date if prev else None
prev_agenda = bool(prev_telechat)
e = TelechatDocEvent()
e.type = "scheduled_for_telechat"
e.by = by
e.doc = doc
e.telechat_date = new_telechat_date
if on_agenda != prev_agenda:
if on_agenda:
e.desc = "Placed on agenda for telechat - %s by %s" % (
new_telechat_date, by.name)
else:
e.desc = "Removed from agenda for telechat by %s" % by.name
elif on_agenda and new_telechat_date != prev_telechat:
e.desc = "Telechat date has been changed to <b>%s</b> from <b>%s</b> by %s" % (new_telechat_date, prev_telechat, by.name)
e.save()

View file

@ -0,0 +1,436 @@
# ballot management (voting, commenting, writeups, ...) for Area
# Directors and Secretariat
import re, os
from datetime import datetime, date, time, timedelta
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.core.urlresolvers import reverse as urlreverse
from django.template.loader import render_to_string
from django.template import RequestContext
from django import forms
from django.utils.html import strip_tags
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from ietf.utils.mail import send_mail_text, send_mail_preformatted
from ietf.ietfauth.decorators import group_required
from ietf.idtracker.templatetags.ietf_filters import in_group
from ietf.ietfauth.decorators import has_role
from mails import email_secretariat
from utils import *
from group.models import Group, GroupHistory, GroupEvent, save_group_in_history
from name.models import GroupBallotPositionName, CharterDocStateName, GroupStateName
from doc.models import Document, DocEvent, GroupBallotPositionDocEvent, save_document_in_history, WriteupDocEvent
def default_action_text(wg, doc, user):
e = WriteupDocEvent(doc=doc, by=user)
e.by = user
e.type = "changed_action_announcement"
e.desc = "WG action text was changed"
e.text = "The %s (%s) working group " % (wg.name, wg.acronym)
if wg.parent:
e.text += "in the %s " % wg.parent.name
e.text += "of the IETF has been "
if wg.state_id == "proposed":
e.text += "proposed"
else:
e.text += " rechartered"
e.text += ". \nFor additional information, please contact the Area Directors or the working group Chairs."
e.save()
return e
def default_review_text(wg, doc, user):
e = WriteupDocEvent(doc=doc, by=user)
e.by = user
e.type = "changed_review_announcement"
e.desc = "WG review text was changed"
if wg.state_id == "proposed":
e.text = "A charter"
else:
e.text = "A modified charter"
e.text += " has been submitted for the %s (%s) working group \n" % (wg.name, wg.acronym)
if wg.parent:
e.text += "in the %s " % wg.parent.name
e.text += "of the IETF. The charter is provided below for\n"
e.text += "informational purposes only. Please send your comments to the\n"
e.text += "IESG mailing list (iesg at ietf.org) within one week from today."
e.save()
return e
BALLOT_CHOICES = (("yes", "Yes"),
("no", "No"),
("block", "Block"),
("abstain", "Abstain"),
("", "No Record"),
)
def position_to_ballot_choice(position):
for v, label in BALLOT_CHOICES:
if v and getattr(position, v):
return v
return ""
def position_label(position_value):
return dict(BALLOT_CHOICES).get(position_value, "")
class EditPositionForm(forms.Form):
position = forms.ModelChoiceField(queryset=GroupBallotPositionName.objects.all(), widget=forms.RadioSelect, initial="norecord", required=True)
block_comment = forms.CharField(required=False, label="Blocking comment", widget=forms.Textarea)
comment = forms.CharField(required=False, widget=forms.Textarea)
return_to_url = forms.CharField(required=False, widget=forms.HiddenInput)
def clean_blocking(self):
entered_blocking = self.cleaned_data["block_comment"]
entered_pos = self.cleaned_data["position"]
if entered_pos.slug == "block" and not entered_blocking:
raise forms.ValidationError("You must enter a non-empty blocking comment")
return entered_blocking
@group_required('Area_Director','Secretariat')
def edit_position(request, name):
"""Vote and edit comments on Charter as Area Director."""
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('rec_edit_position', name=wglist[0].group.acronym)
else:
raise Http404
doc = get_object_or_404(Document, name=wg.charter.name)
started_process = doc.latest_event(type="started_iesg_process")
ad = login = request.user.get_profile()
if 'HTTP_REFERER' in request.META:
return_to_url = request.META['HTTP_REFERER']
else:
return_to_url = doc.get_absolute_url()
# if we're in the Secretariat, we can select an AD to act as stand-in for
if not has_role(request.user, "Area Director"):
ad_id = request.GET.get('ad')
if not ad_id:
raise Http404()
from person.models import Person
ad = get_object_or_404(Person, pk=ad_id)
old_pos = doc.latest_event(GroupBallotPositionDocEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time)
if request.method == 'POST':
form = EditPositionForm(request.POST)
if form.is_valid():
# save the vote
clean = form.cleaned_data
if clean['return_to_url']:
return_to_url = clean['return_to_url']
pos = GroupBallotPositionDocEvent(doc=doc, by=login)
pos.type = "changed_ballot_position"
pos.ad = ad
pos.pos = clean["position"]
pos.comment = clean["comment"].strip()
pos.comment_time = old_pos.comment_time if old_pos else None
pos.block_comment = clean["block_comment"].strip() if pos.pos_id == "block" else ""
pos.block_comment_time = old_pos.block_comment_time if old_pos else None
changes = []
added_events = []
# possibly add discuss/comment comments to history trail
# so it's easy to see
old_comment = old_pos.comment if old_pos else ""
if pos.comment != old_comment:
pos.comment_time = pos.time
changes.append("comment")
if pos.comment:
e = DocEvent(doc=doc)
e.by = ad # otherwise we can't see who's saying it
e.type = "added_comment"
e.desc = "[Ballot comment]\n" + pos.comment
added_events.append(e)
old_block_comment = old_pos.block_comment if old_pos else ""
if pos.block_comment != old_block_comment:
pos.block_comment_time = pos.time
changes.append("block_comment")
if pos.block_comment:
e = DocEvent(doc=doc, by=login)
e.by = ad # otherwise we can't see who's saying it
e.type = "added_comment"
e.desc = "[Ballot blocking comment]\n" + pos.block_comment
added_events.append(e)
# figure out a description
if not old_pos and pos.pos.slug != "norecord":
pos.desc = u"[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.name)
elif old_pos and pos.pos != old_pos.pos:
pos.desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (pos.ad.name, pos.pos.name, old_pos.pos.name)
if not pos.desc and changes:
pos.desc = u"Ballot %s text updated for %s" % (u" and ".join(changes), ad.name)
# only add new event if we actually got a change
if pos.desc:
if login != ad:
pos.desc += u" by %s" % login.name
pos.save()
for e in added_events:
e.save() # save them after the position is saved to get later id
doc.time = pos.time
doc.save()
if request.POST.get("send_mail"):
qstr = "?return_to_url=%s" % return_to_url
if request.GET.get('ad'):
qstr += "&ad=%s" % request.GET.get('ad')
return HttpResponseRedirect(urlreverse("rec_send_ballot_comment", kwargs=dict(name=wg.acronym)) + qstr)
else:
return HttpResponseRedirect(return_to_url)
else:
initial = {}
if old_pos:
initial['position'] = old_pos.pos.slug
initial['block_comment'] = old_pos.block_comment
initial['comment'] = old_pos.comment
if return_to_url:
initial['return_to_url'] = return_to_url
form = EditPositionForm(initial=initial)
return render_to_response('wgrecord/edit_position.html',
dict(doc=doc,
wg=wg,
form=form,
ad=ad,
return_to_url=return_to_url,
old_pos=old_pos,
),
context_instance=RequestContext(request))
@group_required('Area_Director','Secretariat')
def send_ballot_comment(request, name):
"""Email Charter ballot comment for area director."""
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('rec_send_ballot_comment', name=wglist[0].group.acronym)
else:
raise Http404
doc = wg.charter
started_process = doc.latest_event(type="started_iesg_process")
if not started_process:
raise Http404()
ad = login = request.user.get_profile()
return_to_url = request.GET.get('return_to_url')
if not return_to_url:
return_to_url = doc.get_absolute_url()
if 'HTTP_REFERER' in request.META:
back_url = request.META['HTTP_REFERER']
else:
back_url = doc.get_absolute_url()
# if we're in the Secretariat, we can select an AD to act as stand-in for
if not has_role(request.user, "Area Director"):
ad_id = request.GET.get('ad')
if not ad_id:
raise Http404()
from person.models import Person
ad = get_object_or_404(Person, pk=ad_id)
pos = doc.latest_event(GroupBallotPositionDocEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time)
if not pos:
raise Http404()
subj = []
d = ""
if pos.pos_id == "block" and pos.block_comment:
d = pos.block_comment
subj.append("BLOCKING COMMENT")
c = ""
if pos.comment:
c = pos.comment
subj.append("COMMENT")
ad_name_genitive = ad.name + "'" if ad.name.endswith('s') else ad.name + "'s"
subject = "%s %s on %s" % (ad_name_genitive, pos.pos.name if pos.pos else "No Position", doc.name + "-" + doc.rev)
if subj:
subject += ": (with %s)" % " and ".join(subj)
doc.filename = doc.name # compatibility attributes
doc.revision_display = doc.rev
body = render_to_string("wgrecord/ballot_comment_mail.txt",
dict(block_comment=d, comment=c, ad=ad.name, doc=doc, pos=pos.pos))
frm = ad.formatted_email()
to = "The IESG <iesg@ietf.org>"
if request.method == 'POST':
cc = [x.strip() for x in request.POST.get("cc", "").split(',') if x.strip()]
if request.POST.get("cc_state_change") and doc.notify:
cc.extend(doc.notify.split(','))
send_mail_text(request, to, frm, subject, body, cc=", ".join(cc))
return HttpResponseRedirect(return_to_url)
return render_to_response('wgrecord/send_ballot_comment.html',
dict(doc=doc,
subject=subject,
body=body,
frm=frm,
to=to,
ad=ad,
can_send=d or c,
back_url=back_url,
),
context_instance=RequestContext(request))
class AnnouncementTextForm(forms.Form):
announcement_text = forms.CharField(widget=forms.Textarea, required=True)
def clean_announcement_text(self):
return self.cleaned_data["announcement_text"].replace("\r", "")
@group_required('Area_Director','Secretariat')
def announcement_text(request, name, ann):
"""Editing of announcement text"""
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('rec_announcement_text', name=wglist[0].group.acronym)
else:
raise Http404
doc = wg.charter
login = request.user.get_profile()
if ann == "action":
existing = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
elif ann == "review":
existing = doc.latest_event(WriteupDocEvent, type="changed_review_announcement")
if not existing:
if ann == "action":
existing = default_action_text(wg, doc, login)
elif ann == "review":
existing = default_review_text(wg, doc, login)
form = AnnouncementTextForm(initial=dict(announcement_text=existing.text))
if request.method == 'POST':
form = AnnouncementTextForm(request.POST)
if form.is_valid():
t = form.cleaned_data['announcement_text']
if t != existing.text:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_%s_announcement" % ann
e.desc = "WG %s text was changed" % ann
e.text = t
e.save()
doc.time = e.time
doc.save()
return redirect('record_view', name=wg.acronym)
return render_to_response('wgrecord/announcement_text.html',
dict(doc=doc,
announcement=ann,
back_url=doc.get_absolute_url(),
announcement_text_form=form,
),
context_instance=RequestContext(request))
@group_required('Secretariat')
def approve_ballot(request, name):
"""Approve ballot, changing state, copying charter"""
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('rec_approve_ballot', name=wglist[0].group.acronym)
else:
raise Http404
doc = wg.charter
login = request.user.get_profile()
e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
if not e:
announcement = default_action_text(wg, doc, login)
else:
announcement = e.text
if request.method == 'POST':
new_state = GroupStateName.objects.get(slug="active")
new_charter_state = CharterDocStateName.objects.get(slug="approved")
save_document_in_history(doc)
save_group_in_history(wg)
prev_state = wg.state
prev_charter_state = doc.charter_state
wg.state = new_state
doc.charter_state = new_charter_state
e = DocEvent(doc=doc, by=login)
e.type = "iesg_approved"
e.desc = "IESG has approved the charter"
e.save()
change_description = e.desc + " and WG state has been changed to %s" % new_state.name
e = log_state_changed(request, doc, login, prev_state)
wg.time = e.time
wg.save()
filename = os.path.join(doc.get_file_path(), doc.name+"-"+doc.rev+".txt")
try:
source = open(filename, 'rb')
raw_content = source.read()
doc.rev = next_approved_revision(doc.rev)
new_filename = os.path.join(doc.get_file_path(), doc.name+"-"+doc.rev+".txt")
destination = open(new_filename, 'wb+')
destination.write(raw_content)
destination.close()
except IOError:
raise Http404
doc.save()
email_secretariat(request, wg, "state-%s" % doc.charter_state_id, change_description)
# send announcement
send_mail_preformatted(request, announcement)
return HttpResponseRedirect(doc.get_absolute_url())
return render_to_response('wgrecord/approve_ballot.html',
dict(doc=doc,
announcement=announcement,
wg=wg),
context_instance=RequestContext(request))

474
ietf/wgrecord/views_edit.py Normal file
View file

@ -0,0 +1,474 @@
# Copyright The IETF Trust 2011, All Rights Reserved
import re, os, string
from datetime import datetime, date, time, timedelta
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.forms.util import ErrorList
from django.core.exceptions import ObjectDoesNotExist
from utils import log_state_changed, log_info_changed, update_telechat
from mails import email_secretariat
from ietf.ietfauth.decorators import group_required
from ietf.iesg.models import TelechatDates
from doc.models import Document, DocHistory, DocEvent, save_document_in_history, TelechatDocEvent, InitialReviewDocEvent
from name.models import CharterDocStateName, GroupStateName, GroupTypeName, DocTypeName, RoleName
from person.models import Person, Email
from group.models import Group, GroupEvent, GroupHistory, GroupURL, Role, RoleHistory, save_group_in_history
from utils import add_wg_comment
from views_search import json_emails
class ChangeStateForm(forms.Form):
charter_state = forms.ModelChoiceField(CharterDocStateName.objects.all(), label="Charter state", empty_label=None, required=True)
state = forms.ModelChoiceField(GroupStateName.objects.filter(slug__in=["proposed", "active", "conclude"]), label="WG state", empty_label=None, required=True)
confirm_state = forms.BooleanField(widget=forms.HiddenInput, required=False, initial=True)
initial_time = forms.IntegerField(initial=1, label="Review time", help_text="(in weeks)", required=False)
message = forms.CharField(widget=forms.Textarea, help_text="Message the the secretariat", required=False)
comment = forms.CharField(widget=forms.Textarea, help_text="Comment for the WG history", required=False)
def __init__(self, *args, **kwargs):
if 'queryset' in kwargs:
qs = kwargs.pop('queryset')
else:
qs = None
super(ChangeStateForm, self).__init__(*args, **kwargs)
if qs:
self.fields['charter_state'].queryset = qs
@group_required('Area_Director','Secretariat')
def change_state(request, name):
"""Change state of WG and charter, notifying parties as necessary
and logging the change as a comment."""
# Get WG by acronym, redirecting if there's a newer acronym
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('wg_change_state', name=wglist[0].group.acronym)
else:
raise Http404
# Get charter
charter = wg.charter if wg.charter else None
initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review")
login = request.user.get_profile()
if request.method == 'POST':
form = ChangeStateForm(request.POST)
if form.is_valid():
if initial_review and form.cleaned_data['charter_state'].slug != "infrev" and initial_review.expires > datetime.now() and not form.cleaned_data['confirm_state']:
form._errors['charter_state'] = "warning"
else:
state = form.cleaned_data['state']
charter_state = form.cleaned_data['charter_state']
comment = form.cleaned_data['comment']
message = form.cleaned_data['message']
change = False
if charter:
# The WG has a charter
if charter_state != charter.charter_state:
# Charter state changed
change = True
save_document_in_history(charter)
prev = charter.charter_state
charter.charter_state = charter_state
e = log_state_changed(request, charter, login, prev, comment)
charter.time = datetime.now()
charter.save()
else:
# WG does not yet have a charter
if charter_state != "infrev":
# This is an error
raise Http404
if state != wg.state:
# WG state changed
change = True
save_group_in_history(wg)
prev = wg.state
wg.state = state
wg.save()
if change:
if charter:
messages = {}
messages['extrev'] = "The WG has been set to External review by %s. Please schedule discussion for the next IESG telechat." % login.name
if message != "":
email_secretariat(request, wg, "state-%s" % charter.charter_state_id, message)
if charter.charter_state_id == "extrev":
email_secretariat(request, wg, "state-%s" % charter.charter_state_id, messages['extrev'])
if form.cleaned_data["charter_state"] == "infrev":
e = DocEvent()
e.type = "started_iesg_process"
e.by = login
e.doc = charter
e.desc = "IESG process started in state <b>%s</b>" % charter.charter_state.name
e.save()
if form.cleaned_data["initial_time"] != 0:
e = InitialReviewDocEvent()
e.type = "initial_review"
e.by = login
e.doc = charter
e.expires = datetime.now() + timedelta(weeks=form.cleaned_data["initial_time"])
e.desc = "Initial review time expires %s" % e.expires.strftime("%Y-%m-%d")
e.save()
return redirect('record_view', name=wg.acronym)
else:
if wg.state_id != "proposed":
states = CharterDocStateName.objects.filter(slug__in=["infrev", "intrev", "extrev", "iesgrev", "approved"])
form = ChangeStateForm(queryset=states, initial=dict(charter_state=charter.charter_state_id, state=wg.state_id))
else:
form = ChangeStateForm(initial=dict(charter_state=charter.charter_state_id, state=wg.state_id))
group_hists = GroupHistory.objects.filter(group=wg).exclude(state=wg.state).order_by("-time")[:1]
if group_hists:
prev_state = group_hists[0].state
else:
prev_state = None
if charter:
charter_hists = DocHistory.objects.filter(doc__name=charter.name).exclude(charter_state=charter.charter_state).order_by("-time")[:1]
if charter_hists:
prev_charter_state = charter_hists[0].charter_state
else:
prev_charter_state = None
else:
prev_charter_state = None
return render_to_response('wgrecord/change_state.html',
dict(form=form,
wg=wg,
login=login,
prev_state=prev_state,
prev_charter_state=prev_charter_state),
context_instance=RequestContext(request))
class EditInfoForm(forms.Form):
name = forms.CharField(max_length=255, label="WG Name", required=True)
acronym = forms.CharField(max_length=8, label="WG Acronym", required=True)
confirm_acronym = forms.BooleanField(widget=forms.HiddenInput, required=False, initial=True)
chairs = forms.CharField(max_length=255, label="WG Chairs", help_text="Type in a name", required=False)
secretaries = forms.CharField(max_length=255, label="WG Secretaries", help_text="Type in a name", required=False)
techadv = forms.CharField(max_length=255, label="WG Technical Advisors", help_text="Type in a name", required=False)
ad = forms.ModelChoiceField(Person.objects.filter(email__role__name__in=("ad", "ex-ad")).order_by('email__role__name', 'name'), label="Shepherding AD", empty_label="-", required=False)
parent = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), label="IETF Area", empty_label="-", required=False)
list_email = forms.CharField(max_length=64, required=False)
list_subscribe = forms.CharField(max_length=255, required=False)
list_archive = forms.CharField(max_length=255, required=False)
urls = forms.CharField(widget=forms.Textarea, label="Additional URLs", help_text="Format: http://site/url (optional description). Separate by newline.", required=False)
comments = forms.CharField(widget=forms.Textarea, label="Reason for chartering", required=False)
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False)
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
# fix up ad field
choices = self.fields['ad'].choices
ex_ads = dict((e.pk, e) for e in Person.objects.filter(email__role__name="ex-ad").distinct())
# remove old ones
self.fields['ad'].choices = [t for t in choices if t[0] not in ex_ads]
# telechat choices
dates = TelechatDates.objects.all()[0].dates()
if 'telechat_date' in kwargs['initial']:
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
def not_valid_acronym(value):
try:
Group.objects.get(acronym=value)
except ObjectDoesNotExist:
gh = GroupHistory.objects.filter(acronym=value)
if gh:
return True
else:
return False
return True
@group_required('Area_Director','Secretariat')
def edit_info(request, name=None):
"""Edit or create a WG, notifying parties as
necessary and logging changes as group events."""
import sys
if request.path_info == reverse('wg_edit_info', kwargs={'name': name}):
# Editing. Get group
wg = get_object_or_404(Group, acronym=name)
new_wg = False
elif request.path_info == reverse('wg_create'):
wg = None
new_wg = True
login = request.user.get_profile()
if wg and wg.charter:
e = wg.charter.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
initial_telechat_date = e.telechat_date if e else None
else:
initial_telechat_date = None
if request.method == 'POST':
form = EditInfoForm(request.POST, initial=dict(telechat_date=initial_telechat_date))
if form.is_valid():
if (new_wg or form.cleaned_data['acronym'] != wg.acronym) and not_valid_acronym(form.cleaned_data['acronym']) and not form.cleaned_data['confirm_acronym']:
try:
Group.objects.get(acronym=form.cleaned_data['acronym'])
form._errors['acronym'] = "error"
except ObjectDoesNotExist:
form._errors['acronym'] = "warning"
else:
r = form.cleaned_data
if not new_wg:
gh = save_group_in_history(wg)
else:
# Create WG
wg = Group(name=r["name"],
acronym=r["acronym"],
type=GroupTypeName.objects.get(name="WG"),
state=GroupStateName.objects.get(name="Proposed"))
wg.save()
e = GroupEvent(group=wg, type="proposed")
e.time = datetime.now()
e.by = login
e.desc = "Proposed group"
e.save()
if not wg.charter:
# Create adjoined charter
try:
charter = Document.objects.get(name="charter-ietf-"+r["acronym"])
except ObjectDoesNotExist:
charter = Document(type=DocTypeName.objects.get(name="Charter"),
title=r["name"],
abstract=r["name"],
name="charter-ietf-" + r["acronym"],
)
charter.charter_state = CharterDocStateName.objects.get(slug="infrev")
charter.group = wg
charter.save()
e = DocEvent(doc=charter, type="started_iesg_process")
e.time = datetime.now()
e.by = login
e.desc = "Started IESG process on charter"
e.save()
wg.charter = charter
wg.save()
changes = []
def desc(attr, new, old):
entry = "%(attr)s has been changed to <b>%(new)s</b> from <b>%(old)s</b>"
if new_wg:
entry = "%(attr)s has been changed to <b>%(new)s</b>"
return entry % dict(attr=attr, new=new, old=old)
def get_model_fields_as_dict(obj):
return dict((field.name, getattr(obj, field.name))
for field in obj._meta.fields
if field is not obj._meta.pk)
def diff(attr, name):
v = getattr(wg, attr)
if r[attr] != v:
changes.append(desc(name, r[attr], v))
setattr(wg, attr, r[attr])
if attr == "acronym":
c = wg.charter
save_document_in_history(c)
# copy fields
fields = get_model_fields_as_dict(c)
fields["name"] = "charter-ietf-%s" % (r[attr])
new_c = Document(**fields)
new_c.save()
# Set WG charter to new one
wg.charter = new_c
wg.save()
# Move history
for h in c.history_set.all():
h.doc = new_c
h.save()
# Move events
for e in c.docevent_set.all():
e.doc = new_c
e.save()
# And remove the previous charter entry
#c.delete()
# update the attributes, keeping track of what we're doing
diff('name', "Name")
diff('acronym', "Acronym")
diff('ad', "Shepherding AD")
diff('parent', "IETF Area")
diff('list_email', "Mailing list email")
diff('list_subscribe', "Mailing list subscribe address")
diff('list_archive', "Mailing list archive")
diff('comments', "Comment")
def get_sorted_string(attr, splitter):
if splitter == '\n':
out = sorted(r[attr].splitlines())
else:
out = sorted(r[attr].split(splitter))
if out == ['']:
out = []
return out
# update roles
for attr_role in [('chairs', 'Chair'), ('secretaries', 'Secretary'), ('techadv', 'Tech Advisor')]:
attr = attr_role[0]
rname = attr_role[1]
new = get_sorted_string(attr, ",")
old = map(lambda x: x.email.address, wg.role_set.filter(name__name=rname).order_by('email__address'))
if new != old:
# Remove old roles and save them in history
for role in wg.role_set.filter(name__name=rname):
role.delete()
# Add new ones
rolename = RoleName.objects.get(name=rname)
for e in new:
email = Email.objects.get(address=e)
role = Role(name=rolename, email=email, group=wg)
role.save()
# update urls
new_urls = get_sorted_string('urls', '\n')
old_urls = map(lambda x: x.url + " (" + x.name + ")", wg.groupurl_set.order_by('url'))
if new_urls != old_urls:
# Remove old urls
for u in wg.groupurl_set.all():
u.delete()
# Add new ones
for u in [u for u in new_urls if u != ""]:
m = re.search('(?P<url>.+) \((?P<name>.+)\)', u)
url = GroupURL(url=m.group('url'), name=m.group('name'), group=wg)
url.save()
wg.time = datetime.now()
if changes and not new_wg:
for c in changes:
log_info_changed(request, wg, login, c)
update_telechat(request, wg.charter, login, r['telechat_date'])
wg.save()
if new_wg:
return redirect('wg_change_state', name=wg.acronym)
else:
return redirect('record_view', name=wg.acronym)
else:
if wg:
init = dict(name=wg.name if wg.name else None,
acronym=wg.acronym,
chairs=json_emails(map(lambda x: x.email, wg.role_set.filter(name="Chair"))),
secretaries=json_emails(map(lambda x: x.email, wg.role_set.filter(name="Secr"))),
techadv=json_emails(map(lambda x: x.email, wg.role_set.filter(name="Techadv"))),
charter=wg.charter.name if wg.charter else None,
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,
list_subscribe=wg.list_subscribe if wg.list_subscribe else None,
list_archive=wg.list_archive if wg.list_archive else None,
urls=string.join(map(lambda x: x.url + " (" + x.name + ")", wg.groupurl_set.all()), "\n"),
comments=wg.comments if wg.comments else None,
telechat_date=initial_telechat_date,
)
else:
init = dict(ad=login.id,
)
form = EditInfoForm(initial=init)
return render_to_response('wgrecord/edit_info.html',
dict(wg=wg,
form=form,
user=request.user,
login=login),
context_instance=RequestContext(request))
class ConcludeForm(forms.Form):
comment = forms.CharField(widget=forms.Textarea, label="Instructions", required=False)
@group_required('Area_Director','Secretariat')
def conclude(request, name):
"""Request the closing of a WG, prompting for instructions."""
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('wg_conclude', name=wglist[0].group.acronym)
else:
raise Http404
login = request.user.get_profile()
if request.method == 'POST':
form = ConcludeForm(request.POST)
if form.is_valid():
comment = form.cleaned_data['comment']
save_group_in_history(wg)
wg.state = GroupStateName.objects.get(name="Concluded")
wg.save()
email_secretariat(request, wg, "conclude", comment)
return redirect('record_view', name=wg.acronym)
else:
form = ConcludeForm()
return render_to_response('wgrecord/conclude.html',
dict(form=form,
wg=wg),
context_instance=RequestContext(request))
class AddCommentForm(forms.Form):
comment = forms.CharField(required=True, widget=forms.Textarea)
@group_required('Area_Director','Secretariat')
def add_comment(request, name):
"""Add comment to WG Record."""
wg = get_object_or_404(Group, acronym=name)
login = request.user.get_profile()
if request.method == 'POST':
form = AddCommentForm(request.POST)
if form.is_valid():
c = form.cleaned_data['comment']
add_wg_comment(request, wg, c)
#email_owner(request, doc, doc.ad, login,
# "A new comment added by %s" % login.name)
return redirect('record_view', name=wg.acronym)
else:
form = AddCommentForm()
return render_to_response('wgrecord/add_comment.html',
dict(wg=wg,
form=form),
context_instance=RequestContext(request))

219
ietf/wgrecord/views_rec.py Normal file
View file

@ -0,0 +1,219 @@
# Copyright The IETF Trust 2011, All Rights Reserved
import re, os
from datetime import datetime, time
from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.template.defaultfilters import truncatewords_html
from django.utils import simplejson as json
from django.utils.decorators import decorator_from_middleware
from django.middleware.gzip import GZipMiddleware
from django.core.exceptions import ObjectDoesNotExist
from redesign.doc.models import Document, DocHistory, GroupBallotPositionDocEvent, WriteupDocEvent
from redesign.group.models import Group, GroupHistory
from redesign.person.models import Person
from wgrecord import markup_txt
from django.conf import settings
from wgrecord.utils import *
from ietf.utils.history import find_history_active_at
from ietf.idtracker.templatetags.ietf_filters import format_textarea, fill
def _get_html(key, filename):
f = None
try:
f = open(filename, 'rb')
raw_content = f.read()
except IOError:
return ("Error; cannot read ("+key+")", "")
finally:
if f:
f.close()
content = markup_txt.markup(raw_content)
return content
@decorator_from_middleware(GZipMiddleware)
def record_main(request, name, rev, tab):
if tab is None:
tab = "charter"
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('record_view', name=wglist[0].group.acronym)
else:
raise Http404
if rev != None:
charter = get_charter_for_revision(wg.charter, rev)
gh = get_group_for_revision(wg, rev)
else:
charter = get_charter_for_revision(wg.charter, wg.charter.rev)
gh = get_group_for_revision(wg, wg.charter.rev)
info = {}
info['prev_acronyms'] = list(set(map(lambda x: x.acronym, wg.history_set.exclude(acronym=wg.acronym))))
prev_list_email = list(set(map(lambda x: x.list_email, wg.history_set.exclude(list_email=wg.list_email))))
if prev_list_email != [u'']:
info['prev_list_email'] = prev_list_email
prev_list_subscribe = list(set(map(lambda x: x.list_email, wg.history_set.exclude(list_subscribe=wg.list_subscribe))))
if prev_list_subscribe != [u'']:
info['prev_list_subscribe'] = prev_list_subscribe
prev_list_archive = list(set(map(lambda x: x.list_archive, wg.history_set.exclude(list_archive=wg.list_archive))))
if prev_list_archive != [u'']:
info['prev_list_archive'] = prev_list_archive
info['chairs'] = map(lambda x: x.email.person.name, wg.role_set.filter(name__slug="chair"))
if hasattr(gh, 'rolehistory_set'):
info['history_chairs'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="chair"))
else:
info['history_chairs'] = map(lambda x: x.email.person.name, gh.role_set.filter(name__slug="chair"))
info['secr'] = map(lambda x: x.email.person.name, wg.role_set.filter(name__slug="secr"))
info['techadv'] = map(lambda x: x.email.person.name, wg.role_set.filter(name__slug="techadv"))
if charter:
content = _get_html(
str(charter.name)+"-"+str(charter.rev)+",html",
os.path.join(charter.get_file_path(), charter.name+"-"+charter.rev+".txt"))
active_ads = list(Person.objects.filter(email__role__name="ad",
email__role__group__type="area",
email__role__group__state="active").distinct())
started_process = wg.charter.latest_event(type="started_iesg_process")
latest_positions = []
no_record = []
for p in active_ads:
p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=wg.charter, ad=p).order_by("-time"))
if p_pos != []:
latest_positions.append(p_pos[0])
else:
no_record.append(p)
info = {}
info['positions'] = latest_positions
info['pos_yes'] = filter(lambda x: x.pos_id == "yes", latest_positions)
info['pos_no'] = filter(lambda x: x.pos_id == "no", latest_positions)
info['pos_block'] = filter(lambda x: x.pos_id == "block", latest_positions)
info['pos_abstain'] = filter(lambda x: x.pos_id == "abstain", latest_positions)
info['pos_no_record'] = no_record
# Get annoucement texts
review_ann = wg.charter.latest_event(WriteupDocEvent, type="changed_review_announcement")
info['review_text'] = review_ann.text if review_ann else ""
action_ann = wg.charter.latest_event(WriteupDocEvent, type="changed_action_announcement")
info['action_text'] = action_ann.text if action_ann else ""
else:
content = ""
versions = _get_versions(wg.charter) # Important: wg.charter not charter
history = _get_history(wg)
info['last_update'] = history[0]['date']
charter_text_url = charter.get_txt_url()
template = "wgrecord/record_tab_%s" % tab
return render_to_response(template + ".html",
{'content':content,
'charter':charter, 'info':info, 'wg':wg, 'tab':tab,
'rev': rev if rev else charter.rev, 'gh': gh,
'snapshot': rev, 'charter_text_url': charter_text_url,
'history': history, 'versions': versions,
},
context_instance=RequestContext(request));
def _get_history(wg):
results = []
for e in wg.groupevent_set.all().select_related('by').order_by('-time', 'id'):
info = {}
charter_history = find_history_active_at(wg.charter, e.time)
info['version'] = charter_history.rev if charter_history else wg.charter.rev
info['text'] = e.desc
info['by'] = e.by.name
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
info['snipped'] = info['textSnippet'][-3:] == "..."
results.append({'comment':e, 'info':info, 'date':e.time, 'group': wg, 'is_com':True})
for e in wg.charter.docevent_set.all().order_by('-time'):
info = {}
charter_history = find_history_active_at(wg.charter, e.time)
info['version'] = charter_history.rev if charter_history else wg.charter.rev
info['text'] = e.desc
info['by'] = e.by.name
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
info['snipped'] = info['textSnippet'][-3:] == "..."
if e.type == "new_revision":
if charter_history:
charter = get_charter_for_revision(wg.charter, charter_history.rev)
else:
charter = get_charter_for_revision(wg.charter, wg.charter.rev)
prev_charter = get_charter_for_revision(wg.charter, prev_revision(charter.rev))
results.append({'comment':e, 'info':info, 'date':e.time, 'group': wg,
'charter': charter, 'prev_charter': prev_charter,
'txt_url': wg.charter.get_txt_url(),
'is_rev':True})
else:
results.append({'comment':e, 'info':info, 'date':e.time, 'group': wg, 'is_com':True})
# convert plain dates to datetimes (required for sorting)
for x in results:
if not isinstance(x['date'], datetime):
if x['date']:
x['date'] = datetime.combine(x['date'], time(0,0,0))
else:
x['date'] = datetime(1970,1,1)
results.sort(key=lambda x: x['date'])
results.reverse()
return results
def _get_versions(charter, include_replaced=True):
ov = []
for r in sorted(list(set(charter.history_set.values_list('rev', flat=True)))):
if r != "":
d = get_charter_for_revision(charter, r)
if d.rev != charter.rev:
ov.append({"name":d.name, "rev":d.rev, "date":d.time})
if charter.rev != "":
ov.append({"name": charter.name, "rev": charter.rev, "date":charter.time})
return ov
def record_ballot(request, name):
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('record_view', name=wglist[0].group.acronym)
else:
raise Http404
doc = wg.charter
if not doc:
raise Http404
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 = []
no_record = []
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])
else:
no_record.append(p)
info = {}
info['positions'] = latest_positions
info['pos_yes'] = filter(lambda x: x.pos_id == "yes", latest_positions)
info['pos_no'] = filter(lambda x: x.pos_id == "no", latest_positions)
info['pos_block'] = filter(lambda x: x.pos_id == "block", latest_positions)
info['pos_abstain'] = filter(lambda x: x.pos_id == "abstain", latest_positions)
info['pos_no_record'] = no_record
return render_to_response('wgrecord/record_ballot.html', {'info':info, 'wg':wg, 'doc': doc}, context_instance=RequestContext(request))

View file

@ -0,0 +1,256 @@
# Copyright The IETF Trust 2011, All Rights Reserved
import re, os
from django import forms
from django.shortcuts import render_to_response, redirect
from django.db.models import Q
from django.template import RequestContext
from django.views.decorators.cache import cache_page
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponsePermanentRedirect
from redesign.doc.models import Document
from redesign.name.models import GroupStateName, CharterDocStateName
from redesign.group.models import Group
from redesign.person.models import Person, Email
from django.conf import settings
from django.utils import simplejson
class SearchForm(forms.Form):
name = forms.CharField(required=False)
by = forms.ChoiceField(choices=[(x,x) for x in ('acronym','state','ad','area','anyfield', 'eacronym')], required=False, initial='wg', label='Foobar')
acronym = forms.CharField(required=False)
state = forms.ModelChoiceField(GroupStateName.objects.all(), label="WG state", empty_label="any state", required=False)
charter_state = forms.ModelChoiceField(CharterDocStateName.objects.all(), label="Charter state", empty_label="any state", required=False)
ad = forms.ChoiceField(choices=(), required=False)
area = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), empty_label="any area", required=False)
anyfield= forms.CharField(required=False)
eacronym = forms.CharField(required=False)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
responsible = Document.objects.values_list('ad', flat=True).distinct()
active_ads = list(Person.objects.filter(email__role__name="ad",
email__role__group__type="area",
email__role__group__state="active").distinct())
inactive_ads = list(Person.objects.filter(pk__in=responsible)
.exclude(pk__in=[x.pk for x in active_ads]))
extract_last_name = lambda x: x.name_parts()[3]
active_ads.sort(key=extract_last_name)
inactive_ads.sort(key=extract_last_name)
self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.name) for ad in active_ads] + [('', '------------------')] + [(ad.pk, ad.name) for ad in inactive_ads]
def clean_name(self):
value = self.cleaned_data.get('name','')
return value
def clean(self):
q = self.cleaned_data
# Reset query['by'] if needed
for k in ('acronym', 'ad', 'area', 'anyfield', 'eacronym'):
if (q['by'] == k) and not q[k]:
q['by'] = None
if (q['by'] == 'state') and not (q['state'] or q['charter_state']):
q['by'] = None
# Reset other fields
for k in ('acronym', 'ad', 'area', 'anyfield', 'eacronym'):
if q['by'] != k:
self.data[k] = ""
q[k] = ""
if q['by'] != 'state':
self.data['state'] = ""
self.data['charter_state'] = ""
q['state'] = ""
q['charter_state'] = ""
return q
def search_query(query_original, sort_by=None):
query = dict(query_original.items())
# Non-ASCII strings don't match anything; this check
# is currently needed to avoid complaints from MySQL.
for k in ['name','acronym','anyfield','eacronym']:
try:
tmp = str(query.get(k, ''))
except:
query[k] = '*NOSUCH*'
# Search
MAX = 500
maxReached = False
results = Group.objects.filter(type="wg")
prefix = ""
q_objs = []
# name
if query["name"]:
results = results.filter(name__icontains=query["name"])
# radio choices
by = query["by"]
if by == "acronym":
results = results.filter(acronym__icontains=query["acronym"])
elif by == "state":
q_objs = []
if query['state']:
q_objs.append(Q(state=query['state']))
if query['charter_state']:
q_objs.append(Q(charter__charter_state=query['charter_state']))
results = results.filter(*q_objs)
elif by == "ad":
results = results.filter(ad=query["ad"])
elif by == "area":
results = results.filter(parent=query["area"])
elif by == "anyfield":
q_objs = []
q_objs.append(Q(name__icontains=query['anyfield']))
q_objs.append(Q(acronym__icontains=query['anyfield']))
q_objs.append(Q(state__name__icontains=query['anyfield']))
q_objs.append(Q(charter__charter_state__name__icontains=query['anyfield']))
q_objs.append(Q(ad__name__icontains=query['anyfield']))
q_objs.append(Q(parent__name__icontains=query['anyfield']))
q_objs.append(Q(history_set__acronym__icontains=query['anyfield']))
results = list(results.filter(reduce(lambda x, y: x | y, q_objs)))
# Search charter texts
m = re.compile(query['anyfield'], re.IGNORECASE)
for g in Group.objects.filter(type="wg"):
charter = g.charter
try:
file = open(os.path.join(charter.get_file_path(), charter.name+"-"+charter.rev+".txt"))
for line in file:
if m.search(line):
results.append(g)
break
except IOError:
pass # Pass silently for files not found
elif by == "eacronym":
results = results.filter(history_set__acronym__icontains=query["eacronym"]).distinct()
results = list(results[:MAX])
if len(results) == MAX:
maxReached = True
# sort
def sort_key(g):
res = []
if sort_by == "acronym":
res.append(g.acronym)
elif sort_by == "name":
res.append(g.name)
elif sort_by == "date":
res.append(str(g.time or datetime.date(1990, 1, 1)))
elif sort_by == "status":
res.append(g.charter.charter_state)
return res
results.sort(key=sort_key)
meta = {}
if maxReached:
meta['max'] = MAX
if query['by']:
meta['advanced'] = True
return (results,meta)
def generate_query_string(request, ignore_list):
"""Recreates the parameter string from the given request, and
returns it as a string.
Any parameter names present in ignore_list shall not be put
in the result string.
"""
params = []
for i in request.GET:
if not i in ignore_list:
params.append(i + "=" + request.GET[i])
return "?" + "&".join(params)
def search_results(request):
if len(request.REQUEST.items()) == 0:
return search_main(request)
form = SearchForm(dict(request.REQUEST.items()))
if not form.is_valid():
return HttpResponseBadRequest("form not valid?", mimetype="text/plain")
sort_by = None
if "sortBy" in request.GET:
sort_by = request.GET["sortBy"]
(results,meta) = search_query(form.cleaned_data, sort_by)
meta['searching'] = True
meta['by'] = form.cleaned_data['by']
meta['rqps'] = generate_query_string(request, ['sortBy'])
# With a later Django we can do this from the template (incude with tag)
# Pass the headers and their sort key names
meta['hdrs'] = [{'htitle': 'Acronym', 'htype':'acronym'},
{'htitle': 'Name', 'htype':'name'},
{'htitle': 'Date', 'htype':'date'},
{'htitle': 'Status', 'htype':'status', 'colspan':'2'},
]
if 'ajax' in request.REQUEST and request.REQUEST['ajax']:
return render_to_response('wgrecord/search_results.html', {'recs':results, 'meta':meta}, context_instance=RequestContext(request))
elif len(results)==1:
wg = results[0]
return redirect('record_view', name=wg.acronym)
else:
return render_to_response('wgrecord/search_main.html', {'form':form, 'recs':results,'meta':meta}, context_instance=RequestContext(request))
def search_main(request):
form = SearchForm()
return render_to_response('wgrecord/search_main.html', {'form':form}, context_instance=RequestContext(request))
def by_ad(request, name):
ad_id = None
ad_name = None
for p in Person.objects.filter(email__role__name__in=("ad", "ex-ad")):
if name == p.name.lower().replace(" ", "."):
ad_id = p.id
ad_name = p.name
break
if not ad_id:
raise Http404
form = SearchForm({'by':'ad','ad':ad_id})
if not form.is_valid():
raise ValueError("form did not validate")
(results,meta) = search_query(form.cleaned_data)
meta['searching'] = True
meta['by'] = form.cleaned_data['by']
meta['rqps'] = generate_query_string(request, ['sortBy'])
# With a later Django we can do this from the template (incude with tag)
# Pass the headers and their sort key names
meta['hdrs'] = [{'htitle': 'Acronym', 'htype':'acronym'},
{'htitle': 'Name', 'htype':'name'},
{'htitle': 'Date', 'htype':'date'},
{'htitle': 'Status', 'htype':'status', 'colspan':'2'},
]
results.sort(key=lambda g: str(g.time or datetime.date(1990, 1, 1)), reverse=True)
return render_to_response('wgrecord/by_ad.html', {'form':form, 'recs':results,'meta':meta, 'ad_name':ad_name}, context_instance=RequestContext(request))
def in_process(request):
results = Group.objects.filter(type="wg",
charter__charter_state__in=['infrev', 'intrev', 'extrev', 'iesgrev']).order_by('-time')
meta = {}
meta['searching'] = True
meta['by'] = 'state'
meta['rqps'] = generate_query_string(request, ['sortBy'])
# With a later Django we can do this from the template (incude with tag)
# Pass the headers and their sort key names
meta['hdrs'] = [{'htitle': 'Acronym', 'htype':'acronym'},
{'htitle': 'Name', 'htype':'name'},
{'htitle': 'Date', 'htype':'date'},
{'htitle': 'Status', 'htype':'status', 'colspan':'2'},
]
return render_to_response('wgrecord/in_process.html', {'recs':results,'meta':meta}, context_instance=RequestContext(request))
def json_emails(list):
result = []
for p in list:
result.append({"id": p.address + "", "name":p.person.name + " &lt;" + p.address + "&gt;"})
return simplejson.dumps(result)
def search_person(request):
if request.method == 'GET':
emails = Email.objects.filter(person__name__istartswith=request.GET.get('q','')).order_by('person__name')
return HttpResponse(json_emails(emails), mimetype='application/json')

View file

@ -0,0 +1,80 @@
# Copyright The IETF Trust 2011, All Rights Reserved
import os
from datetime import datetime
from django.http import HttpResponseRedirect, Http404
from django import forms
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.core.exceptions import ObjectDoesNotExist
from group.models import save_group_in_history
from doc.models import Document, DocHistory, DocEvent, save_document_in_history
from redesign.group.models import Group
from django.conf import settings
from utils import next_revision
class UploadForm(forms.Form):
txt = forms.FileField(label=".txt format", required=True)
def __init__(self, *args, **kwargs):
super(UploadForm, self).__init__(*args, **kwargs)
def save(self, wg):
for ext in ['txt']:
fd = self.cleaned_data[ext]
if not fd:
continue
filename = os.path.join(settings.CHARTER_PATH, 'charter-ietf-%s-%s.%s' % (wg.acronym, next_revision(wg.charter.rev), ext))
destination = open(filename, 'wb+')
for chunk in fd.chunks():
destination.write(chunk)
destination.close()
def submit(request, name):
# Get WG by acronym, redirecting if there's a newer acronym
try:
wg = Group.objects.get(acronym=name)
except ObjectDoesNotExist:
wglist = GroupHistory.objects.filter(acronym=name)
if wglist:
return redirect('charter_submit', name=wglist[0].group.acronym)
else:
raise Http404
# Get charter
charter = wg.charter if wg.charter else None
login = request.user.get_profile()
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
save_document_in_history(charter)
# Also save group history so we can search for it
save_group_in_history(wg)
charter.rev = next_revision(charter.rev)
e = DocEvent()
e.type = "new_revision"
e.by = login
e.doc = charter
e.desc = "New version available: <b>%s</b>" % (charter.filename_with_rev())
e.save()
# Save file on disk
form.save(wg)
charter.time = datetime.now()
charter.save()
return HttpResponseRedirect(reverse('record_view', kwargs={'name': wg.acronym}))
else:
form = UploadForm()
return render_to_response('wgrecord/submit.html',
{'form': form,
'next_rev': next_revision(wg.charter.rev),
'wg': wg},
context_instance=RequestContext(request))

View file

@ -10,6 +10,7 @@ from redesign.person.models import Email, Person
from redesign.util import admin_link
import datetime, os
from ietf import settings
class DocumentInfo(models.Model):
"""Any kind of document. Draft, RFC, Charter, IPR Statement, Liaison Statement"""
@ -26,6 +27,7 @@ class DocumentInfo(models.Model):
iesg_state = models.ForeignKey(IesgDocStateName, verbose_name="IESG state", blank=True, null=True) #
iana_state = models.ForeignKey(IanaDocStateName, verbose_name="IANA state", blank=True, null=True)
rfc_state = models.ForeignKey(RfcDocStateName, verbose_name="RFC state", blank=True, null=True)
charter_state = models.ForeignKey(CharterDocStateName, verbose_name="IESG charter state", blank=True, null=True)
# Other
abstract = models.TextField()
rev = models.CharField(verbose_name="revision", max_length=16, blank=True)
@ -46,9 +48,17 @@ class DocumentInfo(models.Model):
elif self.type_id in ("agenda", "minutes", "slides"):
meeting = self.name.split("-")[1]
return os.path.join(settings.AGENDA_PATH, meeting, self.type_id) + "/"
elif self.type_id == "charter":
return settings.CHARTER_PATH
else:
raise NotImplemented
def get_txt_url(self):
if self.type.slug == "charter":
return "http://www.ietf.org/charters/"
else:
raise NotImplemented
class Meta:
abstract = True
def author_list(self):
@ -87,11 +97,14 @@ class Document(DocumentInfo):
def get_absolute_url(self):
name = self.name
if self.state == "rfc":
aliases = self.docalias_set.filter(name__startswith="rfc")
if aliases:
name = aliases[0].name
return urlreverse('doc_view', kwargs={ 'name': name })
if self.type.slug == "charter":
return urlreverse('record_view', kwargs={ 'name': self.group.acronym })
else:
if self.state == "rfc":
aliases = self.docalias_set.filter(name__startswith="rfc")
if aliases:
name = aliases[0].name
return urlreverse('doc_view', kwargs={ 'name': name })
def file_tag(self):
return u"<%s>" % self.filename_with_rev()
@ -140,6 +153,7 @@ class DocHistory(DocumentInfo):
doc = models.ForeignKey(Document, related_name="history_set")
# Django 1.2 won't let us define these in the base class, so we have
# to repeat them
name = models.CharField(max_length=255) # We need to save the name for charters
related = models.ManyToManyField('DocAlias', through=RelatedDocHistory, blank=True)
authors = models.ManyToManyField(Email, through=DocHistoryAuthor, blank=True)
def __unicode__(self):
@ -154,6 +168,7 @@ def save_document_in_history(doc):
# copy fields
fields = get_model_fields_as_dict(doc)
fields["doc"] = doc
fields["name"] = doc.name
dochist = DocHistory(**fields)
dochist.save()
@ -210,6 +225,11 @@ EVENT_TYPES = [
("requested_resurrect", "Requested resurrect"),
("completed_resurrect", "Completed resurrect"),
("published_rfc", "Published RFC"),
# Charter events
("initial_review", "Set initial review time"),
("changed_review_announcement", "Changed WG Review text"),
("changed_action_announcement", "Changed WG Action text"),
# IESG events
("started_iesg_process", "Started IESG process on document"),
@ -273,3 +293,14 @@ class TelechatDocEvent(DocEvent):
telechat_date = models.DateField(blank=True, null=True)
returning_item = models.BooleanField(default=False)
# Charter ballot events
class GroupBallotPositionDocEvent(DocEvent):
ad = models.ForeignKey(Person)
pos = models.ForeignKey(GroupBallotPositionName, verbose_name="position", default="norecord")
block_comment = models.TextField(help_text="Blocking comment if position is comment", blank=True)
block_comment_time = models.DateTimeField(help_text="Blocking comment was written", blank=True, null=True)
comment = models.TextField(help_text="Non-blocking comment", blank=True)
comment_time = models.DateTimeField(help_text="Time non-blocking comment was written", blank=True, null=True)
class InitialReviewDocEvent(DocEvent):
expires = models.DateTimeField(blank=True, null=True)

View file

@ -9,5 +9,5 @@ class GroupAdmin(admin.ModelAdmin):
admin.site.register(Group, GroupAdmin)
admin.site.register(GroupHistory)
admin.site.register(GroupURL)
admin.site.register(Role)

View file

@ -13,7 +13,6 @@ class GroupInfo(models.Model):
state = models.ForeignKey(GroupStateName, null=True)
type = models.ForeignKey(GroupTypeName, null=True)
parent = models.ForeignKey('Group', blank=True, null=True)
iesg_state = models.ForeignKey(IesgGroupStateName, verbose_name="IESG state", blank=True, null=True)
ad = models.ForeignKey(Person, blank=True, null=True)
list_email = models.CharField(max_length=64, blank=True)
list_subscribe = models.CharField(max_length=255, blank=True)
@ -40,11 +39,39 @@ class Group(GroupInfo):
# to select group history from this table.
class GroupHistory(GroupInfo):
group = models.ForeignKey(Group, related_name='history_set')
charter = models.ForeignKey('doc.Document', related_name='chartered_group_history_set', blank=True, null=True)
class Meta:
verbose_name_plural="group histories"
def save_group_in_history(group):
def get_model_fields_as_dict(obj):
return dict((field.name, getattr(obj, field.name))
for field in obj._meta.fields
if field is not obj._meta.pk)
# copy fields
fields = get_model_fields_as_dict(group)
del fields["charter"] # Charter is saved canonically on Group
fields["group"] = group
grouphist = GroupHistory(**fields)
grouphist.save()
# save RoleHistory
for role in group.role_set.all():
rh = RoleHistory(name=role.name, group=grouphist, email=role.email)
rh.save()
# copy many to many
for field in group._meta.many_to_many:
if not field.rel.through:
# just add the attributes
rel = getattr(grouphist, field.name)
for item in getattr(group, field.name).all():
rel.add(item)
return grouphist
class GroupURL(models.Model):
group = models.ForeignKey(Group)
name = models.CharField(max_length=255)
@ -62,10 +89,16 @@ class GroupMilestone(models.Model):
class Meta:
ordering = ['expected_due_date']
GROUP_EVENT_CHOICES = [("proposed", "Proposed group"),
("started", "Started group"),
("concluded", "Concluded group"),
]
GROUP_EVENT_CHOICES = [
# core events
("proposed", "Proposed group"),
("started", "Started group"),
("concluded", "Concluded group"),
# misc group events
("added_comment", "Added comment"),
("changed_record", "Changed record metadata"),
]
class GroupEvent(models.Model):
"""An occurrence for a group, used for tracking who, when and what."""

View file

@ -56,7 +56,7 @@ class Area(Group):
#status = models.ForeignKey(AreaStatus)
@property
def status_id(self):
return { "active": 1, "dormant": 2, "conclude": 3 }[self.state_id]
return { "active": 1, "dormant": 2, "conclude": 3, "proposed": 4 }[self.state_id]
#comments = models.TextField(blank=True)
#last_modified_date = models.DateField(auto_now=True)
@property
@ -121,7 +121,7 @@ class IETFWG(Group):
#status = models.ForeignKey(WGStatus)
@property
def status_id(self):
return { "active": 1, "dormant": 2, "conclude": 3 }[self.state_id]
return { "active": 1, "dormant": 2, "conclude": 3, "proposed": 4 }[self.state_id]
#area_director = models.ForeignKey(AreaDirector, null=True)
#meeting_scheduled = models.CharField(blank=True, max_length=3)
@property

View file

@ -81,7 +81,7 @@ for o in Announcement.objects.all().select_related('announced_to', 'announced_fr
if o.nomcom:
nomcom = Group.objects.filter(role__name="chair",
role__email__person=old_person_to_person(o.nomcom_chair.person),
role__email__person__id=o.nomcom_chair.person.pk,
acronym__startswith="nomcom").exclude(acronym="nomcom").get()
message.related_groups.add(nomcom)

View file

@ -52,6 +52,7 @@ def alias_doc(name, doc):
return alias
type_draft = name(DocTypeName, "draft", "Draft")
type_charter = name(DocTypeName, "charter", "Charter")
stream_mapping = get_stream_mapping()
@ -123,6 +124,29 @@ ballot_position_mapping["recuse"] = ballot_position_mapping['Recuse']
ballot_position_mapping[None] = ballot_position_mapping["No Record"]
ballot_position_mapping["Undefined"] = ballot_position_mapping["No Record"]
group_ballot_position_mapping = {
'No': name(GroupBallotPositionName, 'no', 'No'),
'Yes': name(GroupBallotPositionName, 'yes', 'Yes'),
'Abstain': name(GroupBallotPositionName, 'abstain', 'Abstain'),
'Block': name(GroupBallotPositionName, 'block', 'Block'),
'No Record': name(GroupBallotPositionName, 'norecord', 'No record'),
}
group_ballot_position_mapping["no"] = group_ballot_position_mapping['No']
group_ballot_position_mapping["yes"] = group_ballot_position_mapping['Yes']
group_ballot_position_mapping["block"] = group_ballot_position_mapping['Block']
group_ballot_position_mapping["abstain"] = group_ballot_position_mapping['Abstain']
group_ballot_position_mapping[None] = group_ballot_position_mapping["No Record"]
group_ballot_position_mapping["Undefined"] = group_ballot_position_mapping["No Record"]
charter_state_names = dict(
notrev=name(CharterDocStateName, slug="notrev", name="Not currently under review", desc="The proposed WG is not being considered at this time. A proposed WG will remain in this state until an AD moves it to Informal IESG review."),
infrev=name(CharterDocStateName, slug="infrev", name="Informal IESG review", desc="This is the initial state when an AD creates a WG. The normal next state is Internal review if the idea is accepted, or Not currently under review if the idea is abandoned."),
intrev=name(CharterDocStateName, slug="intrev", name="Internal review", 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"),
extrev=name(CharterDocStateName, slug="extrev", name="External review", 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."),
iesgrev=name(CharterDocStateName, slug="iesgrev", name="IESG review", 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."),
approved=name(CharterDocStateName, slug="approved", name="Approved", desc="The WG is approved by the IESG."),
)
substate_mapping = {
"External Party": name(DocInfoTagName, 'extpty', "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.', 3),
"Revised ID Needed": name(DocInfoTagName, 'need-rev', "Revised ID Needed", 'An updated ID is needed to address the issues that have been raised.', 5),
@ -310,7 +334,7 @@ def import_from_idinternal(d, idinternal):
found = False
for p in positions:
if not BallotPositionDocEvent.objects.filter(doc=d, type="changed_ballot_position", pos=position, ad=iesg_login_to_person(p.ad)):
if not d.docevent_set.filter(type="changed_ballot_position", ballotposition__pos=position, ballotposition__ad=iesg_login_to_person(p.ad)):
login = p.ad
found = True
break

View file

@ -14,6 +14,7 @@ management.setup_environ(settings)
from redesign.group.models import *
from redesign.name.models import *
from redesign.doc.models import Document
from redesign.name.utils import name
from redesign.importing.utils import old_person_to_person
from ietf.idtracker.models import AreaGroup, IETFWG, Area, AreaGroup, Acronym, AreaWGURL, IRTF, ChairsHistory, Role, AreaDirector
@ -74,13 +75,6 @@ secretariat_group.state = state_names["active"]
secretariat_group.type = type_names["ietf"]
secretariat_group.save()
# create RSOC for use with roles
rsoc_group, _ = Group.objects.get_or_create(acronym="rsoc")
rsoc_group.name = "RFC Series Oversight Committee"
rsoc_group.state = state_names["active"]
rsoc_group.type = type_names["ietf"]
rsoc_group.save()
system = Person.objects.get(name="(System)")
@ -187,7 +181,6 @@ for o in Area.objects.all():
# FIXME: missing fields from old: extra_email_addresses
# IETFWG, AreaGroup
for o in IETFWG.objects.all().order_by("pk"):
print "importing IETFWG", o.pk, o.group_acronym.acronym
@ -260,6 +253,21 @@ for o in IETFWG.objects.all().order_by("pk"):
if l in ("none", "not available"):
l = ""
group.list_archive = l
try:
charter = Document.objects.get(name="charter-ietf-" + o.group_acronym.acronym)
except Document.DoesNotExist:
charter = Document(type = name(DocTypeName, "charter", "Charter"),
title = o.group_acronym.name,
abstract= o.group_acronym.name,
name="charter-ietf-" + o.group_acronym.acronym,
)
charter.rev = "01"
charter.charter_state = name(CharterDocStateName, slug="approved", name="Approved", desc="The WG is approved by the IESG.")
charter.group = group
charter.save()
group.charter = charter
group.comments = o.comments.strip() if o.comments else ""
group.save()

View file

@ -46,7 +46,7 @@ authorized_role = name(RoleName, "auth", "Authorized Individual")
# SDOAuthorizedIndividual
for o in SDOAuthorizedIndividual.objects.all().order_by("pk"):
print "importing SDOAuthorizedIndividual", o.pk, unicode(o.sdo).encode("utf-8"), unicode(o.person).encode("utf-8")
print "importing SDOAuthorizedIndividual", o.pk, o.sdo, o.person
group = Group.objects.get(name=o.sdo.sdo_name, type="sdo")
email = get_or_create_email(o, create_fake=False)
@ -55,7 +55,7 @@ for o in SDOAuthorizedIndividual.objects.all().order_by("pk"):
# LiaisonManagers
for o in LiaisonManagers.objects.all().order_by("pk"):
print "importing LiaisonManagers", o.pk, unicode(o.sdo).encode("utf-8"), unicode(o.person).encode("utf-8")
print "importing LiaisonManagers", o.pk, o.sdo, o.person
group = Group.objects.get(name=o.sdo.sdo_name, type="sdo")
email = Email.objects.get(address__iexact=o.person.email(priority=o.email_priority)[1])
@ -80,10 +80,13 @@ for o in OldRole.objects.all().order_by('pk'):
acronym = "ietf"
role = adm_director_role
group = Group.objects.get(acronym=acronym)
email = get_or_create_email(o, create_fake=False)
Role.objects.get_or_create(name=role, group=group, email=email)
try:
group = Group.objects.get(acronym=acronym)
email = get_or_create_email(o, create_fake=False)
Role.objects.get_or_create(name=role, group=group, email=email)
except:
pass #FIXME this fails for group RSOC
# WGEditor
for o in WGEditor.objects.all():
@ -261,7 +264,6 @@ for o in IESGHistory.objects.all().order_by('meeting__start_date', 'pk'):
state=existing.state,
type=existing.type,
parent=existing.parent,
iesg_state=existing.iesg_state,
ad=existing.ad,
list_email=existing.list_email,
list_subscribe=existing.list_subscribe,

View file

@ -7,7 +7,6 @@ class NameAdmin(admin.ModelAdmin):
admin.site.register(GroupTypeName, NameAdmin)
admin.site.register(GroupStateName, NameAdmin)
admin.site.register(IesgGroupStateName, NameAdmin)
admin.site.register(RoleName, NameAdmin)
admin.site.register(DocStreamName, NameAdmin)
admin.site.register(DocStateName, NameAdmin)
@ -16,11 +15,9 @@ admin.site.register(WgDocStateName, NameAdmin)
admin.site.register(IesgDocStateName, NameAdmin)
admin.site.register(IanaDocStateName, NameAdmin)
admin.site.register(RfcDocStateName, NameAdmin)
admin.site.register(CharterDocStateName, NameAdmin)
admin.site.register(DocTypeName, NameAdmin)
admin.site.register(DocInfoTagName, NameAdmin)
admin.site.register(StdLevelName, NameAdmin)
admin.site.register(IntendedStdLevelName, NameAdmin)
admin.site.register(BallotPositionName, NameAdmin)
admin.site.register(SessionStatusName, NameAdmin)
admin.site.register(TimeSlotTypeName, NameAdmin)
admin.site.register(ConstraintName, NameAdmin)

View file

@ -20,11 +20,6 @@ class GroupStateName(NameModel):
"""BOF, Proposed, Active, Dormant, Concluded"""
class GroupTypeName(NameModel):
"""IETF, Area, WG, RG, Team, etc."""
class IesgGroupStateName(NameModel):
"""Informal IESG review, Internal review, External review, IESG review,
WG exists, Not currently under review, Informal IESG recharter review,
Internal recharter review, External recharter review, IESG recharter
review """
class RoleName(NameModel):
"""AD, Chair"""
class DocStreamName(NameModel):
@ -48,6 +43,9 @@ class IanaDocStateName(NameModel):
class RfcDocStateName(NameModel):
"""Missref, Edit, RFC-Editor, Auth48, Auth, Published; ISR,
ISR-Auth, ISR-Timeout;"""
class CharterDocStateName(NameModel):
"""Not currently under review, Informal IESG review, Internal
review, External review, IESG review, Approved"""
class DocTypeName(NameModel):
"""Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email,
Review, Issue, Wiki"""
@ -62,6 +60,8 @@ class IntendedStdLevelName(NameModel):
Practice, Historic, ..."""
class BallotPositionName(NameModel):
""" Yes, NoObjection, Abstain, Discuss, Recuse """
class GroupBallotPositionName(NameModel):
""" Yes, No, Block, Abstain """
class MeetingTypeName(NameModel):
"""IETF, Interim"""
class SessionStatusName(NameModel):

View file

@ -33,9 +33,7 @@ class IDState(IesgDocStateName):
DEAD = 99
DO_NOT_PUBLISH_STATES = (33, 34)
objects = TranslatingManager(dict(pk="order",
document_state_id="order",
document_state_id__in="order__in"))
objects = TranslatingManager(dict(pk="order"))
def from_object(self, base):
for f in base._meta.fields:

113
static/css/token-input.css Normal file
View file

@ -0,0 +1,113 @@
/* Example tokeninput style #1: Token vertical list*/
ul.token-input-list {
overflow: hidden;
height: auto !important;
height: 1%;
width: 400px;
border: 1px solid #999;
cursor: text;
font-size: 12px;
font-family: Verdana;
z-index: 999;
margin: 0;
padding: 0;
background-color: #fff;
list-style-type: none;
clear: left;
}
ul.token-input-list li {
list-style-type: none;
}
ul.token-input-list li input {
border: 0;
width: 350px;
padding: 3px 8px;
background-color: white;
-webkit-appearance: caret;
}
li.token-input-token {
overflow: hidden;
height: auto !important;
height: 1%;
margin: 3px;
padding: 3px 5px;
background-color: #d0efa0;
color: #000;
font-weight: bold;
cursor: default;
display: block;
}
li.token-input-token p {
float: left;
padding: 0;
margin: 0;
}
li.token-input-token span {
float: right;
color: #777;
cursor: pointer;
}
li.token-input-selected-token {
background-color: #08844e;
color: #fff;
}
li.token-input-selected-token span {
color: #bbb;
}
div.token-input-dropdown {
position: absolute;
width: 400px;
background-color: #fff;
overflow: hidden;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
cursor: default;
font-size: 12px;
font-family: Verdana;
z-index: 1;
}
div.token-input-dropdown p {
margin: 0;
padding: 5px;
font-weight: bold;
color: #777;
}
div.token-input-dropdown ul {
margin: 0;
padding: 0;
}
div.token-input-dropdown ul li {
background-color: #fff;
padding: 3px;
list-style-type: none;
}
div.token-input-dropdown ul li.token-input-dropdown-item {
background-color: #fafafa;
}
div.token-input-dropdown ul li.token-input-dropdown-item2 {
background-color: #fff;
}
div.token-input-dropdown ul li em {
font-weight: bold;
font-style: normal;
}
div.token-input-dropdown ul li.token-input-selected-dropdown-item {
background-color: #d0efa0;
}

View file

@ -74,6 +74,52 @@ function showBallot(draftName, editPositionUrl) {
function editBallot(editPositionUrl) {
window.open(editPositionUrl);
}
function showRecBallot(acronym, editPositionUrl) {
var handleEditPosition = function() {
IETF.ballotDialog.hide();
window.location = IETF.editPositionUrl;
};
var handleClose = function() {
IETF.ballotDialog.hide();
};
var el;
if (!IETF.ballotDialog) {
el = document.createElement("div");
el.innerHTML = '<div id="ballot_dialog" style="visibility:hidden;"><div class="hd">Positions for <span id="ballot_dialog_name">draft-ietf-foo-bar</span></span></div><div class="bd"> <div id="ballot_dialog_body" style="overflow-y:scroll; height:400px;"></div> </div></div>';
document.getElementById("ietf-extras").appendChild(el);
var buttons = [{text:"Close", handler:handleClose, isDefault:true}];
if (("Area_Director" in IETF.user_groups) ||
("Secretariat" in IETF.user_groups)) {
buttons.unshift({text:"Edit Position", handler:handleEditPosition});
}
var kl = [new YAHOO.util.KeyListener(document, {keys:27}, handleClose)]
IETF.ballotDialog = new YAHOO.widget.Dialog("ballot_dialog", {
visible:false, draggable:false, close:true, modal:true,
width:"860px", fixedcenter:true, constraintoviewport:true,
buttons: buttons, keylisteners:kl});
IETF.ballotDialog.render();
}
document.getElementById("ballot_dialog_name").innerHTML = acronym;
IETF.editPositionUrl = editPositionUrl;
IETF.ballotDialog.show();
el = document.getElementById("ballot_dialog_body");
el.innerHTML = "Loading...";
YAHOO.util.Connect.asyncRequest('GET',
"/wgrecord/"+acronym+"/_ballot.data",
{ success: function(o) { el.innerHTML = (o.responseText !== undefined) ? o.responseText : "?"; },
failure: function(o) { el.innerHTML = "Error: "+o.status+" "+o.statusText; },
argument: null
}, null);
}
function editRecBallot(editPositionUrl) {
window.open(editPositionUrl);
}
function showStream(dialogTitle, infoStreamUrl) {
var handleClose = function() {
IETF.streamDialog.hide();

View file

@ -0,0 +1,793 @@
/*
* jQuery Plugin: Tokenizing Autocomplete Text Entry
* Version 1.5.0
*
* Copyright (c) 2009 James Smith (http://loopj.com)
* Licensed jointly under the GPL and MIT licenses,
* choose which one suits your project best!
*
*/
(function ($) {
// Default settings
var DEFAULT_SETTINGS = {
hintText: "Type in a search term",
noResultsText: "No results",
searchingText: "Searching...",
deleteText: "&times;",
searchDelay: 300,
minChars: 1,
tokenLimit: null,
jsonContainer: null,
method: "GET",
contentType: "json",
queryParam: "q",
tokenDelimiter: ",",
preventDuplicates: false,
prePopulate: null,
processPrePopulate: false,
animateDropdown: true,
onResult: null,
onAdd: null,
onDelete: null,
idPrefix: "token-input-"
};
// Default classes to use when theming
var DEFAULT_CLASSES = {
tokenList: "token-input-list",
token: "token-input-token",
tokenDelete: "token-input-delete-token",
selectedToken: "token-input-selected-token",
highlightedToken: "token-input-highlighted-token",
dropdown: "token-input-dropdown",
dropdownItem: "token-input-dropdown-item",
dropdownItem2: "token-input-dropdown-item2",
selectedDropdownItem: "token-input-selected-dropdown-item",
inputToken: "token-input-input-token"
};
// Input box position "enum"
var POSITION = {
BEFORE: 0,
AFTER: 1,
END: 2
};
// Keys "enum"
var KEY = {
BACKSPACE: 8,
TAB: 9,
ENTER: 13,
ESCAPE: 27,
SPACE: 32,
PAGE_UP: 33,
PAGE_DOWN: 34,
END: 35,
HOME: 36,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
NUMPAD_ENTER: 108,
COMMA: 188
};
// Additional public (exposed) methods
var methods = {
init: function(url_or_data_or_function, options) {
var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
return this.each(function () {
$(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
});
},
clear: function() {
this.data("tokenInputObject").clear();
return this;
},
add: function(item) {
this.data("tokenInputObject").add(item);
return this;
},
remove: function(item) {
this.data("tokenInputObject").remove(item);
return this;
}
}
// Expose the .tokenInput function to jQuery as a plugin
$.fn.tokenInput = function (method) {
// Method calling and initialization logic
if(methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else {
return methods.init.apply(this, arguments);
}
};
// TokenList class for each input
$.TokenList = function (input, url_or_data, settings) {
//
// Initialization
//
// Configure the data source
if(typeof(url_or_data) === "string") {
// Set the url to query against
settings.url = url_or_data;
// Make a smart guess about cross-domain if it wasn't explicitly specified
if(settings.crossDomain === undefined) {
if(settings.url.indexOf("://") === -1) {
settings.crossDomain = false;
} else {
settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]);
}
}
} else if(typeof(url_or_data) === "object") {
// Set the local data to search through
settings.local_data = url_or_data;
}
// Build class names
if(settings.classes) {
// Use custom class names
settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
} else if(settings.theme) {
// Use theme-suffixed default class names
settings.classes = {};
$.each(DEFAULT_CLASSES, function(key, value) {
settings.classes[key] = value + "-" + settings.theme;
});
} else {
settings.classes = DEFAULT_CLASSES;
}
// Save the tokens
var saved_tokens = [];
// Keep track of the number of tokens in the list
var token_count = 0;
// Basic cache to save on db hits
var cache = new $.TokenList.Cache();
// Keep track of the timeout, old vals
var timeout;
var input_val;
// Create a new text input an attach keyup events
var input_box = $("<input type=\"text\" autocomplete=\"off\">")
.css({
outline: "none"
})
.attr("id", settings.idPrefix + input.id)
.focus(function () {
if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
show_dropdown_hint();
}
})
.blur(function () {
hide_dropdown();
$(this).val("");
})
.bind("keyup keydown blur update", resize_input)
.keydown(function (event) {
var previous_token;
var next_token;
switch(event.keyCode) {
case KEY.LEFT:
case KEY.RIGHT:
case KEY.UP:
case KEY.DOWN:
if(!$(this).val()) {
previous_token = input_token.prev();
next_token = input_token.next();
if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
// Check if there is a previous/next token and it is selected
if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
deselect_token($(selected_token), POSITION.BEFORE);
} else {
deselect_token($(selected_token), POSITION.AFTER);
}
} else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
// We are moving left, select the previous token if it exists
select_token($(previous_token.get(0)));
} else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
// We are moving right, select the next token if it exists
select_token($(next_token.get(0)));
}
} else {
var dropdown_item = null;
if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
dropdown_item = $(selected_dropdown_item).next();
} else {
dropdown_item = $(selected_dropdown_item).prev();
}
if(dropdown_item.length) {
select_dropdown_item(dropdown_item);
}
return false;
}
break;
case KEY.BACKSPACE:
previous_token = input_token.prev();
if(!$(this).val().length) {
if(selected_token) {
delete_token($(selected_token));
} else if(previous_token.length) {
select_token($(previous_token.get(0)));
}
return false;
} else if($(this).val().length === 1) {
hide_dropdown();
} else {
// set a timeout just long enough to let this function finish.
setTimeout(function(){do_search();}, 5);
}
break;
case KEY.TAB:
case KEY.ENTER:
case KEY.NUMPAD_ENTER:
case KEY.COMMA:
if(selected_dropdown_item) {
add_token($(selected_dropdown_item).data("tokeninput"));
return false;
}
break;
case KEY.ESCAPE:
hide_dropdown();
return true;
default:
if(String.fromCharCode(event.which)) {
// set a timeout just long enough to let this function finish.
setTimeout(function(){do_search();}, 5);
}
break;
}
});
// Keep a reference to the original input box
var hidden_input = $(input)
.hide()
.val("")
.focus(function () {
input_box.focus();
})
.blur(function () {
input_box.blur();
});
// Keep a reference to the selected token and dropdown item
var selected_token = null;
var selected_token_index = 0;
var selected_dropdown_item = null;
// The list to store the token items in
var token_list = $("<ul />")
.addClass(settings.classes.tokenList)
.click(function (event) {
var li = $(event.target).closest("li");
if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
toggle_select_token(li);
} else {
// Deselect selected token
if(selected_token) {
deselect_token($(selected_token), POSITION.END);
}
// Focus input box
input_box.focus();
}
})
.mouseover(function (event) {
var li = $(event.target).closest("li");
if(li && selected_token !== this) {
li.addClass(settings.classes.highlightedToken);
}
})
.mouseout(function (event) {
var li = $(event.target).closest("li");
if(li && selected_token !== this) {
li.removeClass(settings.classes.highlightedToken);
}
})
.insertBefore(hidden_input);
// The token holding the input box
var input_token = $("<li />")
.addClass(settings.classes.inputToken)
.appendTo(token_list)
.append(input_box);
// The list to store the dropdown items in
var dropdown = $("<div>")
.addClass(settings.classes.dropdown)
.appendTo("body")
.hide();
// Magic element to help us resize the text input
var input_resizer = $("<tester/>")
.insertAfter(input_box)
.css({
position: "absolute",
top: -9999,
left: -9999,
width: "auto",
fontSize: input_box.css("fontSize"),
fontFamily: input_box.css("fontFamily"),
fontWeight: input_box.css("fontWeight"),
letterSpacing: input_box.css("letterSpacing"),
whiteSpace: "nowrap"
});
// Pre-populate list if items exist
hidden_input.val("");
var li_data = settings.prePopulate || hidden_input.data("pre");
if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
li_data = settings.onResult.call(hidden_input, li_data);
}
if(li_data && li_data.length) {
$.each(li_data, function (index, value) {
insert_token(value);
checkTokenLimit();
});
}
//
// Public functions
//
this.clear = function() {
token_list.children("li").each(function() {
if ($(this).children("input").length === 0) {
delete_token($(this));
}
});
}
this.add = function(item) {
add_token(item);
}
this.remove = function(item) {
token_list.children("li").each(function() {
if ($(this).children("input").length === 0) {
var currToken = $(this).data("tokeninput");
var match = true;
for (var prop in item) {
if (item[prop] !== currToken[prop]) {
match = false;
break;
}
}
if (match) {
delete_token($(this));
}
}
});
}
//
// Private functions
//
function checkTokenLimit() {
if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
input_box.hide();
hide_dropdown();
return;
} else {
input_box.focus();
}
}
function resize_input() {
if(input_val === (input_val = input_box.val())) {return;}
// Enter new content into resizer and resize input accordingly
var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
input_resizer.html(escaped);
input_box.width(input_resizer.width() + 30);
}
function is_printable_character(keycode) {
return ((keycode >= 48 && keycode <= 90) || // 0-1a-z
(keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * .
(keycode >= 186 && keycode <= 192) || // ; = , - . / ^
(keycode >= 219 && keycode <= 222)); // ( \ ) '
}
// Inner function to a token to the list
function insert_token(item) {
var this_token = $("<li><p>"+ item.name +"</p></li>")
.addClass(settings.classes.token)
.insertBefore(input_token);
// The 'delete token' button
$("<span>" + settings.deleteText + "</span>")
.addClass(settings.classes.tokenDelete)
.appendTo(this_token)
.click(function () {
delete_token($(this).parent());
return false;
});
// Store data on the token
var token_data = {"id": item.id, "name": item.name};
$.data(this_token.get(0), "tokeninput", item);
// Save this token for duplicate checking
saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
selected_token_index++;
// Update the hidden input
var token_ids = $.map(saved_tokens, function (el) {
return el.id;
});
hidden_input.val(token_ids.join(settings.tokenDelimiter));
token_count += 1;
return this_token;
}
// Add a token to the token list based on user input
function add_token (item) {
var callback = settings.onAdd;
// See if the token already exists and select it if we don't want duplicates
if(token_count > 0 && settings.preventDuplicates) {
var found_existing_token = null;
token_list.children().each(function () {
var existing_token = $(this);
var existing_data = $.data(existing_token.get(0), "tokeninput");
if(existing_data && existing_data.id === item.id) {
found_existing_token = existing_token;
return false;
}
});
if(found_existing_token) {
select_token(found_existing_token);
input_token.insertAfter(found_existing_token);
input_box.focus();
return;
}
}
// Insert the new tokens
insert_token(item);
checkTokenLimit();
// Clear input box
input_box.val("");
// Don't show the help dropdown, they've got the idea
hide_dropdown();
// Execute the onAdd callback if defined
if($.isFunction(callback)) {
callback.call(hidden_input,item);
}
}
// Select a token in the token list
function select_token (token) {
token.addClass(settings.classes.selectedToken);
selected_token = token.get(0);
// Hide input box
input_box.val("");
// Hide dropdown if it is visible (eg if we clicked to select token)
hide_dropdown();
}
// Deselect a token in the token list
function deselect_token (token, position) {
token.removeClass(settings.classes.selectedToken);
selected_token = null;
if(position === POSITION.BEFORE) {
input_token.insertBefore(token);
selected_token_index--;
} else if(position === POSITION.AFTER) {
input_token.insertAfter(token);
selected_token_index++;
} else {
input_token.appendTo(token_list);
selected_token_index = token_count;
}
// Show the input box and give it focus again
input_box.focus();
}
// Toggle selection of a token in the token list
function toggle_select_token(token) {
var previous_selected_token = selected_token;
if(selected_token) {
deselect_token($(selected_token), POSITION.END);
}
if(previous_selected_token === token.get(0)) {
deselect_token(token, POSITION.END);
} else {
select_token(token);
}
}
// Delete a token from the token list
function delete_token (token) {
// Remove the id from the saved list
var token_data = $.data(token.get(0), "tokeninput");
var callback = settings.onDelete;
var index = token.prevAll().length;
if(index > selected_token_index) index--;
// Delete the token
token.remove();
selected_token = null;
// Show the input box and give it focus again
input_box.focus();
// Remove this token from the saved list
saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
if(index < selected_token_index) selected_token_index--;
// Update the hidden input
var token_ids = $.map(saved_tokens, function (el) {
return el.id;
});
hidden_input.val(token_ids.join(settings.tokenDelimiter));
token_count -= 1;
if(settings.tokenLimit !== null) {
input_box
.show()
.val("")
.focus();
}
// Execute the onDelete callback if defined
if($.isFunction(callback)) {
callback.call(hidden_input,token_data);
}
}
// Hide and clear the results dropdown
function hide_dropdown () {
dropdown.hide().empty();
selected_dropdown_item = null;
}
function show_dropdown() {
dropdown
.css({
position: "absolute",
top: $(token_list).offset().top + $(token_list).outerHeight(),
left: $(token_list).offset().left,
zindex: 999
})
.show();
}
function show_dropdown_searching () {
if(settings.searchingText) {
dropdown.html("<p>"+settings.searchingText+"</p>");
show_dropdown();
}
}
function show_dropdown_hint () {
if(settings.hintText) {
dropdown.html("<p>"+settings.hintText+"</p>");
show_dropdown();
}
}
// Highlight the query part of the search term
function highlight_term(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
}
// Populate the results dropdown with some results
function populate_dropdown (query, results) {
if(results && results.length) {
dropdown.empty();
var dropdown_ul = $("<ul>")
.appendTo(dropdown)
.mouseover(function (event) {
select_dropdown_item($(event.target).closest("li"));
})
.mousedown(function (event) {
add_token($(event.target).closest("li").data("tokeninput"));
return false;
})
.hide();
$.each(results, function(index, value) {
var this_li = $("<li>" + highlight_term(value.name, query) + "</li>")
.appendTo(dropdown_ul);
if(index % 2) {
this_li.addClass(settings.classes.dropdownItem);
} else {
this_li.addClass(settings.classes.dropdownItem2);
}
if(index === 0) {
select_dropdown_item(this_li);
}
$.data(this_li.get(0), "tokeninput", value);
});
show_dropdown();
if(settings.animateDropdown) {
dropdown_ul.slideDown("fast");
} else {
dropdown_ul.show();
}
} else {
if(settings.noResultsText) {
dropdown.html("<p>"+settings.noResultsText+"</p>");
show_dropdown();
}
}
}
// Highlight an item in the results dropdown
function select_dropdown_item (item) {
if(item) {
if(selected_dropdown_item) {
deselect_dropdown_item($(selected_dropdown_item));
}
item.addClass(settings.classes.selectedDropdownItem);
selected_dropdown_item = item.get(0);
}
}
// Remove highlighting from an item in the results dropdown
function deselect_dropdown_item (item) {
item.removeClass(settings.classes.selectedDropdownItem);
selected_dropdown_item = null;
}
// Do a search and show the "searching" dropdown if the input is longer
// than settings.minChars
function do_search() {
var query = input_box.val().toLowerCase();
if(query && query.length) {
if(selected_token) {
deselect_token($(selected_token), POSITION.AFTER);
}
if(query.length >= settings.minChars) {
show_dropdown_searching();
clearTimeout(timeout);
timeout = setTimeout(function(){
run_search(query);
}, settings.searchDelay);
} else {
hide_dropdown();
}
}
}
// Do the actual search
function run_search(query) {
var cached_results = cache.get(query);
if(cached_results) {
populate_dropdown(query, cached_results);
} else {
// Are we doing an ajax search or local data search?
if(settings.url) {
// Extract exisiting get params
var ajax_params = {};
ajax_params.data = {};
if(settings.url.indexOf("?") > -1) {
var parts = settings.url.split("?");
ajax_params.url = parts[0];
var param_array = parts[1].split("&");
$.each(param_array, function (index, value) {
var kv = value.split("=");
ajax_params.data[kv[0]] = kv[1];
});
} else {
ajax_params.url = settings.url;
}
// Prepare the request
ajax_params.data[settings.queryParam] = query;
ajax_params.type = settings.method;
ajax_params.dataType = settings.contentType;
if(settings.crossDomain) {
ajax_params.dataType = "jsonp";
}
// Attach the success callback
ajax_params.success = function(results) {
if($.isFunction(settings.onResult)) {
results = settings.onResult.call(hidden_input, results);
}
cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
// only populate the dropdown if the results are associated with the active search query
if(input_box.val().toLowerCase() === query) {
populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
}
};
// Make the request
$.ajax(ajax_params);
} else if(settings.local_data) {
// Do the search through local data
var results = $.grep(settings.local_data, function (row) {
return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
});
if($.isFunction(settings.onResult)) {
results = settings.onResult.call(hidden_input, results);
}
cache.add(query, results);
populate_dropdown(query, results);
}
}
}
};
// Really basic cache for the results
$.TokenList.Cache = function (options) {
var settings = $.extend({
max_size: 500
}, options);
var data = {};
var size = 0;
var flush = function () {
data = {};
size = 0;
};
this.add = function (query, results) {
if(size > settings.max_size) {
flush();
}
if(!data[query]) {
size += 1;
}
data[query] = results;
};
this.get = function (query) {
return data[query];
};
};
}(jQuery));

View file

@ -0,0 +1,27 @@
jQuery(document).ready(function () {
var initial_time = jQuery("#id_initial_time").parent().parent()
if (jQuery("#id_charter_state").val() != "infrev") {
initial_time.hide()
}
function setMessageDraft(state) {
if (message[state]) {
if (state == "infrev") {
initial_time.show();
jQuery("#id_initial_time").val(1);
} else {
initial_time.hide();
jQuery("#id_initial_time").val(0);
}
jQuery("#id_message").val(message[state]);
} else {
jQuery("#id_message").val("");
}
}
jQuery("#id_charter_state").click(function (e) {
setMessageDraft(jQuery(this).val());
});
jQuery("#id_charter_state").click();
});

View file

@ -0,0 +1,14 @@
jQuery(document).ready(function () {
function setDiscussWidgetVisibility(block) {
if (block)
jQuery("form.position-form .block_comment-widgets").show();
else
jQuery("form.position-form .block_comment-widgets").hide();
}
jQuery("form.position-form input[name=position]").click(function (e) {
setDiscussWidgetVisibility(jQuery(this).val() == "block");
});
setDiscussWidgetVisibility(jQuery("form.position-form input[name=position][value=block]").is(':checked'));
});