Merge facelift-r8510 branch with trunk. The IPR tool still has some non-facelifted parts and ideosyncracies. Fix a couple of minor bugs (including infamous empty licensing choice) in the IPR code, and port the IPR views to use the render shortcut.
- Legacy-Id: 8896
This commit is contained in:
commit
d0999c8b5e
25
README
Normal file
25
README
Normal file
|
@ -0,0 +1,25 @@
|
|||
This is the "facelift" datatracker branch that uses Twitter Bootstrap for
|
||||
the UI.
|
||||
|
||||
You need to install a few new django extensions:
|
||||
https://pypi.python.org/pypi/django-widget-tweaks
|
||||
https://pypi.python.org/pypi/django-bootstrap3
|
||||
https://pypi.python.org/pypi/django-typogrify
|
||||
|
||||
The meta goal of this effort is: *** NO CHANGES TO THE PYTHON CODE ***
|
||||
|
||||
Whenever changes to the python code are made, they can only fix HTML bugs,
|
||||
or add comments (tagged with "FACELIFT") about functionality that can be
|
||||
removed once the facelift templates become default. Or they need to add
|
||||
functionality that is only called from the new facelift templates.
|
||||
|
||||
Javascript that is only used on one template goes into that template.
|
||||
Javascript that is used by more than one template goes into ietf.js.
|
||||
|
||||
CSS that is only used on one template goes into that template.
|
||||
CSS that is used by more than one template goes into ietf.css. No CSS in the
|
||||
templates or - god forbid - style tags! (And no CSS or HTML styling in
|
||||
python code!!)
|
||||
|
||||
Templates that use jquery or bootstrap plugins include the css in the pagehead
|
||||
block, and the js in the js block.
|
11
TODO
Normal file
11
TODO
Normal file
|
@ -0,0 +1,11 @@
|
|||
Major pieces not facelifted: milestone editing, liaison editing, WG workflow customization
|
||||
|
||||
Use affix for navigation on active_wgs.html
|
||||
|
||||
Figure out why {% if debug %} does not work in the text templates under ietf/templates_facelift/community/public.
|
||||
|
||||
Make django generate HTML5 date inputs or use a js-based datepicker.
|
||||
|
||||
Deferring ballots does not work. (Seems to be an upstream bug.)
|
||||
|
||||
Make tables that are too wide to usefully work on small screens responsive. See http://getbootstrap.com/css/#tables-responsive
|
|
@ -1,5 +1,6 @@
|
|||
from django import template
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
|
||||
from ietf.community.models import CommunityList
|
||||
from ietf.group.models import Role
|
||||
|
@ -9,7 +10,7 @@ register = template.Library()
|
|||
|
||||
|
||||
class CommunityListNode(template.Node):
|
||||
|
||||
|
||||
def __init__(self, user, var_name):
|
||||
self.user = user
|
||||
self.var_name = var_name
|
||||
|
@ -57,13 +58,13 @@ def show_field(field, doc):
|
|||
|
||||
|
||||
class CommunityListViewNode(template.Node):
|
||||
|
||||
|
||||
def __init__(self, clist):
|
||||
self.clist = clist
|
||||
|
||||
def render(self, context):
|
||||
clist = self.clist.resolve(context)
|
||||
if not clist.cached:
|
||||
if settings.DEBUG or not clist.cached:
|
||||
clist.cached = render_to_string('community/raw_view.html',
|
||||
{'cl': clist,
|
||||
'dc': clist.get_display_config()})
|
||||
|
|
88
ietf/doc/fields.py
Normal file
88
ietf/doc/fields.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
import json
|
||||
|
||||
from django.utils.html import escape
|
||||
from django import forms
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.doc.models import Document, DocAlias
|
||||
|
||||
def select2_id_doc_name_json(objs):
|
||||
return json.dumps([{ "id": o.pk, "text": escape(o.name) } for o in objs])
|
||||
|
||||
class SearchableDocumentsField(forms.CharField):
|
||||
"""Server-based multi-select field for choosing documents using
|
||||
select2.js.
|
||||
|
||||
The field uses a comma-separated list of primary keys in a
|
||||
CharField element as its API with some extra attributes used by
|
||||
the Javascript part."""
|
||||
|
||||
def __init__(self,
|
||||
max_entries=None, # max number of selected objs
|
||||
model=Document,
|
||||
hint_text="Type in name to search for document",
|
||||
doc_type="draft",
|
||||
*args, **kwargs):
|
||||
kwargs["max_length"] = 10000
|
||||
self.max_entries = max_entries
|
||||
self.doc_type = doc_type
|
||||
self.model = model
|
||||
|
||||
super(SearchableDocumentsField, self).__init__(*args, **kwargs)
|
||||
|
||||
self.widget.attrs["class"] = "select2-field"
|
||||
self.widget.attrs["data-placeholder"] = hint_text
|
||||
if self.max_entries != None:
|
||||
self.widget.attrs["data-max-entries"] = self.max_entries
|
||||
|
||||
def parse_select2_value(self, value):
|
||||
return [x.strip() for x in value.split(",") if x.strip()]
|
||||
|
||||
def prepare_value(self, value):
|
||||
if not value:
|
||||
value = ""
|
||||
if isinstance(value, basestring):
|
||||
pks = self.parse_select2_value(value)
|
||||
value = self.model.objects.filter(pk__in=pks)
|
||||
filter_args = {}
|
||||
if self.model == DocAlias:
|
||||
filter_args["document__type"] = self.doc_type
|
||||
else:
|
||||
filter_args["type"] = self.doc_type
|
||||
value = value.filter(**filter_args)
|
||||
if isinstance(value, self.model):
|
||||
value = [value]
|
||||
|
||||
self.widget.attrs["data-pre"] = select2_id_doc_name_json(value)
|
||||
|
||||
# doing this in the constructor is difficult because the URL
|
||||
# patterns may not have been fully constructed there yet
|
||||
self.widget.attrs["data-ajax-url"] = urlreverse("ajax_select2_search_docs", kwargs={
|
||||
"doc_type": self.doc_type,
|
||||
"model_name": self.model.__name__.lower()
|
||||
})
|
||||
|
||||
return u",".join(unicode(o.pk) for o in value)
|
||||
|
||||
def clean(self, value):
|
||||
value = super(SearchableDocumentsField, self).clean(value)
|
||||
pks = self.parse_select2_value(value)
|
||||
|
||||
objs = self.model.objects.filter(pk__in=pks)
|
||||
|
||||
found_pks = [str(o.pk) for o in objs]
|
||||
failed_pks = [x for x in pks if x not in found_pks]
|
||||
if failed_pks:
|
||||
raise forms.ValidationError(u"Could not recognize the following documents: {pks}. You can only input documents already registered in the Datatracker.".format(pks=", ".join(failed_pks)))
|
||||
|
||||
if self.max_entries != None and len(objs) > self.max_entries:
|
||||
raise forms.ValidationError(u"You can select at most %s entries only." % self.max_entries)
|
||||
|
||||
return objs
|
||||
|
||||
class SearchableDocAliasesField(SearchableDocumentsField):
|
||||
def __init__(self, model=DocAlias, *args, **kwargs):
|
||||
super(SearchableDocAliasesField, self).__init__(model=model, *args, **kwargs)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import DataMigration
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
|
|
@ -87,35 +87,32 @@ def ballot_icon(context, doc):
|
|||
positions = list(doc.active_ballot().active_ad_positions().items())
|
||||
positions.sort(key=sort_key)
|
||||
|
||||
edit_position_url = ""
|
||||
if has_role(user, "Area Director"):
|
||||
edit_position_url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
|
||||
|
||||
title = "IESG positions (click to show more%s)" % (", right-click to edit position" if edit_position_url else "")
|
||||
|
||||
res = ['<a href="%s" data-popup="%s" data-edit="%s" title="%s" class="ballot-icon"><table>' % (
|
||||
urlreverse("doc_ballot", kwargs=dict(name=doc.name, ballot_id=ballot.pk)),
|
||||
res = ['<a href="%s" data-toggle="modal" data-target="#modal-%d" title="IESG positions (click to show more)" class="ballot-icon"><table>' % (
|
||||
urlreverse("ietf.doc.views_doc.ballot_popup", kwargs=dict(name=doc.name, ballot_id=ballot.pk)),
|
||||
edit_position_url,
|
||||
title
|
||||
)]
|
||||
ballot.pk)]
|
||||
|
||||
res.append("<tr>")
|
||||
|
||||
for i, (ad, pos) in enumerate(positions):
|
||||
if i > 0 and i % 5 == 0:
|
||||
res.append("</tr>")
|
||||
res.append("<tr>")
|
||||
res.append("</tr><tr>")
|
||||
|
||||
c = "position-%s" % (pos.pos.slug if pos else "norecord")
|
||||
|
||||
if user_is_person(user, ad):
|
||||
c += " my"
|
||||
|
||||
res.append('<td class="%s" />' % c)
|
||||
res.append('<td class="%s"></td>' % c)
|
||||
|
||||
res.append("</tr>")
|
||||
res.append("</table></a>")
|
||||
# add sufficient table calls to last row to avoid HTML validation warning
|
||||
while (i + 1) % 5 != 0:
|
||||
res.append('<td class="empty"></td>')
|
||||
i = i + 1
|
||||
|
||||
res.append("</tr></table></a>")
|
||||
# XXX FACELIFT: Loading via href will go away in bootstrap 4.
|
||||
# See http://getbootstrap.com/javascript/#modals-usage
|
||||
res.append('<div id="modal-%d" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"><div class="modal-dialog modal-lg"><div class="modal-content"></div></div></div>' % ballot.pk)
|
||||
|
||||
return "".join(res)
|
||||
|
||||
|
@ -137,7 +134,8 @@ def ballotposition(doc, user):
|
|||
|
||||
|
||||
@register.filter
|
||||
def state_age_colored(doc):
|
||||
# FACELIFT: added flavor argument for styling
|
||||
def state_age_colored(doc, flavor=""):
|
||||
if doc.type_id == 'draft':
|
||||
if not doc.get_state_slug() in ["active", "rfc"]:
|
||||
# Don't show anything for expired/withdrawn/replaced drafts
|
||||
|
@ -156,7 +154,7 @@ def state_age_colored(doc):
|
|||
except IndexError:
|
||||
state_date = datetime.date(1990,1,1)
|
||||
days = (datetime.date.today() - state_date).days
|
||||
# loosely based on
|
||||
# loosely based on
|
||||
# http://trac.tools.ietf.org/group/iesg/trac/wiki/PublishPath
|
||||
if iesg_state == "lc":
|
||||
goal1 = 30
|
||||
|
@ -180,16 +178,26 @@ def state_age_colored(doc):
|
|||
goal1 = 14
|
||||
goal2 = 28
|
||||
if days > goal2:
|
||||
class_name = "ietf-small ietf-highlight-r"
|
||||
if flavor == "facelift":
|
||||
class_name = "label label-danger"
|
||||
else:
|
||||
class_name = "ietf-small ietf-highlight-r"
|
||||
elif days > goal1:
|
||||
class_name = "ietf-small ietf-highlight-y"
|
||||
if flavor == "facelift":
|
||||
class_name = "label label-warning"
|
||||
else:
|
||||
class_name = "ietf-small ietf-highlight-y"
|
||||
else:
|
||||
class_name = "ietf-small"
|
||||
if days > goal1:
|
||||
title = ' title="Goal is <%d days"' % (goal1,)
|
||||
else:
|
||||
title = ''
|
||||
return mark_safe('<span class="%s"%s>(for %d day%s)</span>' % (
|
||||
class_name, title, days, 's' if days != 1 else ''))
|
||||
# It's too bad that this function returns HTML; this makes it hard to
|
||||
# style. For the facelift, I therefore needed to add a new "flavor"
|
||||
# parameter, which is ugly.
|
||||
return mark_safe('<span class="%s"%s>%sfor %d day%s%s</span>' % (
|
||||
class_name, title, '(' if flavor != "facelift" else "", days,
|
||||
's' if days != 1 else '', '(' if flavor != "facelift" else "" ))
|
||||
else:
|
||||
return ""
|
||||
|
|
|
@ -8,9 +8,10 @@ from email.utils import parseaddr
|
|||
|
||||
from ietf.doc.models import ConsensusDocEvent
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
from django.utils.html import escape, fix_ampersands
|
||||
from django.utils.text import wrap
|
||||
from django.template.defaultfilters import truncatewords_html, linebreaksbr, stringfilter, urlize
|
||||
from django.template.defaultfilters import truncatewords_html, linebreaksbr, stringfilter, striptags, urlize
|
||||
from django.template import resolve_variable
|
||||
from django.utils.safestring import mark_safe, SafeData
|
||||
from django.utils.html import strip_tags
|
||||
|
@ -52,12 +53,12 @@ def parse_email_list(value):
|
|||
u'<a href="mailto:joe@example.org">joe@example.org</a>, <a href="mailto:fred@example.com">fred@example.com</a>'
|
||||
|
||||
Parsing a non-string should return the input value, rather than fail:
|
||||
|
||||
|
||||
>>> parse_email_list(['joe@example.org', 'fred@example.com'])
|
||||
['joe@example.org', 'fred@example.com']
|
||||
|
||||
|
||||
Null input values should pass through silently:
|
||||
|
||||
|
||||
>>> parse_email_list('')
|
||||
''
|
||||
|
||||
|
@ -91,7 +92,7 @@ def fix_angle_quotes(value):
|
|||
if "<" in value:
|
||||
value = re.sub("<([\w\-\.]+@[\w\-\.]+)>", "<\1>", value)
|
||||
return value
|
||||
|
||||
|
||||
# there's an "ahref -> a href" in GEN_UTIL
|
||||
# but let's wait until we understand what that's for.
|
||||
@register.filter(name='make_one_per_line')
|
||||
|
@ -103,7 +104,7 @@ def make_one_per_line(value):
|
|||
'a\\nb\\nc'
|
||||
|
||||
Pass through non-strings:
|
||||
|
||||
|
||||
>>> make_one_per_line([1, 2])
|
||||
[1, 2]
|
||||
|
||||
|
@ -114,7 +115,7 @@ def make_one_per_line(value):
|
|||
return re.sub(", ?", "\n", value)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
@register.filter(name='timesum')
|
||||
def timesum(value):
|
||||
"""
|
||||
|
@ -203,7 +204,7 @@ def rfcspace(string):
|
|||
"""
|
||||
string = str(string)
|
||||
if string[:3].lower() == "rfc" and string[3] != " ":
|
||||
return string[:3] + " " + string[3:]
|
||||
return string[:3].upper() + " " + string[3:]
|
||||
else:
|
||||
return string
|
||||
|
||||
|
@ -226,7 +227,7 @@ def rfclink(string):
|
|||
URL for that RFC.
|
||||
"""
|
||||
string = str(string);
|
||||
return "http://tools.ietf.org/html/rfc" + string;
|
||||
return "//tools.ietf.org/html/rfc" + string;
|
||||
|
||||
@register.filter(name='urlize_ietf_docs', is_safe=True, needs_autoescape=True)
|
||||
def urlize_ietf_docs(string, autoescape=None):
|
||||
|
@ -278,7 +279,7 @@ def truncate_ellipsis(text, arg):
|
|||
return escape(text[:num-1])+"…"
|
||||
else:
|
||||
return escape(text)
|
||||
|
||||
|
||||
@register.filter
|
||||
def split(text, splitter=None):
|
||||
return text.split(splitter)
|
||||
|
@ -379,7 +380,7 @@ def linebreaks_lf(text):
|
|||
@register.filter(name='clean_whitespace')
|
||||
def clean_whitespace(text):
|
||||
"""
|
||||
Map all ASCII control characters (0x00-0x1F) to spaces, and
|
||||
Map all ASCII control characters (0x00-0x1F) to spaces, and
|
||||
remove unnecessary spaces.
|
||||
"""
|
||||
text = re.sub("[\000-\040]+", " ", text)
|
||||
|
@ -388,7 +389,7 @@ def clean_whitespace(text):
|
|||
@register.filter(name='unescape')
|
||||
def unescape(text):
|
||||
"""
|
||||
Unescape />/<
|
||||
Unescape />/<
|
||||
"""
|
||||
text = text.replace(">", ">")
|
||||
text = text.replace("<", "<")
|
||||
|
@ -427,7 +428,7 @@ def has_role(user, role_names):
|
|||
@register.filter
|
||||
def stable_dictsort(value, arg):
|
||||
"""
|
||||
Like dictsort, except it's stable (preserves the order of items
|
||||
Like dictsort, except it's stable (preserves the order of items
|
||||
whose sort key is the same). See also bug report
|
||||
http://code.djangoproject.com/ticket/12110
|
||||
"""
|
||||
|
@ -459,7 +460,7 @@ def format_snippet(text, trunc_words=25):
|
|||
full = mark_safe(keep_spacing(collapsebr(linebreaksbr(urlize(sanitize_html(text))))))
|
||||
snippet = truncatewords_html(full, trunc_words)
|
||||
if snippet != full:
|
||||
return mark_safe(u'<div class="snippet">%s<span class="show-all">[show all]</span></div><div style="display:none" class="full">%s</div>' % (snippet, full))
|
||||
return mark_safe(u'<div class="snippet">%s<button class="btn btn-xs btn-default show-all"><span class="fa fa-caret-down"></span></button></div><div class="hidden full">%s</div>' % (snippet, full))
|
||||
return full
|
||||
|
||||
@register.filter
|
||||
|
@ -563,3 +564,62 @@ class WordWrapNode(template.Node):
|
|||
def render(self, context):
|
||||
return wrap(str(self.nodelist.render(context)), int(self.len))
|
||||
|
||||
# FACELIFT: The following filters are only used by the facelift UI:
|
||||
|
||||
@register.filter
|
||||
def pos_to_label(text):
|
||||
"""Return a valid Bootstrap3 label type for a ballot position."""
|
||||
return {
|
||||
'Yes': 'success',
|
||||
'No Objection': 'info',
|
||||
'Abstain': 'warning',
|
||||
'Discuss': 'danger',
|
||||
'Block': 'danger',
|
||||
'Recuse': 'default',
|
||||
}.get(str(text), 'blank')
|
||||
|
||||
@register.filter
|
||||
def capfirst_allcaps(text):
|
||||
"""Like capfirst, except it doesn't lowercase words in ALL CAPS."""
|
||||
result = text
|
||||
i = False
|
||||
for token in re.split("(\W+)", striptags(text)):
|
||||
if not re.match("^[A-Z]+$", token):
|
||||
if not i:
|
||||
result = result.replace(token, token.capitalize())
|
||||
i = True
|
||||
else:
|
||||
result = result.replace(token, token.lower())
|
||||
return result
|
||||
|
||||
@register.filter
|
||||
def lower_allcaps(text):
|
||||
"""Like lower, except it doesn't lowercase words in ALL CAPS."""
|
||||
result = text
|
||||
for token in re.split("(\W+)", striptags(text)):
|
||||
if not re.match("^[A-Z]+$", token):
|
||||
result = result.replace(token, token.lower())
|
||||
return result
|
||||
|
||||
# See https://djangosnippets.org/snippets/2072/ and
|
||||
# https://stackoverflow.com/questions/9939248/how-to-prevent-django-basic-inlines-from-autoescaping
|
||||
@register.filter
|
||||
def urlize_html(html, autoescape=False):
|
||||
"""
|
||||
Returns urls found in an (X)HTML text node element as urls via Django urlize filter.
|
||||
"""
|
||||
try:
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
except ImportError:
|
||||
if settings.DEBUG:
|
||||
raise template.TemplateSyntaxError, "Error in urlize_html The Python BeautifulSoup libraries aren't installed."
|
||||
return html
|
||||
else:
|
||||
soup = BeautifulSoup(html)
|
||||
|
||||
textNodes = soup.findAll(text=True)
|
||||
for textNode in textNodes:
|
||||
urlizedText = urlize(textNode, autoescape=autoescape)
|
||||
textNode.replaceWith(BeautifulSoup(urlizedText))
|
||||
|
||||
return str(soup)
|
||||
|
|
|
@ -43,24 +43,32 @@ area_short_names = {
|
|||
'rai':'RAI'
|
||||
}
|
||||
|
||||
# FACELIFT: Function is called with "facelift" flavor from the new UI code.
|
||||
# The old code (and flavoring) can be remove eventually.
|
||||
@register.simple_tag
|
||||
def wg_menu():
|
||||
res = cache.get('base_left_wgmenu')
|
||||
def wg_menu(flavor=""):
|
||||
res = cache.get('wgmenu' + flavor)
|
||||
if res:
|
||||
return res
|
||||
|
||||
areas = Group.objects.filter(type="area", state="active").order_by('acronym')
|
||||
groups = Group.objects.filter(type="wg", state="active", parent__in=areas).order_by("acronym")
|
||||
wgs = Group.objects.filter(type="wg", state="active", parent__in=areas).order_by("acronym")
|
||||
rgs = Group.objects.filter(type="rg", state="active").order_by("acronym")
|
||||
|
||||
for a in areas:
|
||||
a.short_area_name = area_short_names.get(a.acronym) or a.name
|
||||
if a.short_area_name.endswith(" Area"):
|
||||
a.short_area_name = a.short_area_name[:-len(" Area")]
|
||||
|
||||
a.active_groups = [g for g in groups if g.parent_id == a.id]
|
||||
a.active_groups = [g for g in wgs if g.parent_id == a.id]
|
||||
|
||||
areas = [a for a in areas if a.active_groups]
|
||||
|
||||
res = render_to_string('base/wg_menu.html', {'areas':areas})
|
||||
cache.set('base_left_wgmenu', res, 30*60)
|
||||
if flavor == "facelift":
|
||||
res = render_to_string('base/menu_wg.html', {'areas':areas, 'rgs':rgs})
|
||||
elif flavor == "modal":
|
||||
res = render_to_string('base/menu_wg_modal.html', {'areas':areas, 'rgs':rgs})
|
||||
else:
|
||||
res = render_to_string('base/wg_menu.html', {'areas':areas, 'rgs':rgs})
|
||||
cache.set('wgmenu' + flavor, res, 30*60)
|
||||
return res
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import datetime
|
||||
import json
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] < 7:
|
||||
import unittest2 as unittest
|
||||
|
@ -97,7 +98,7 @@ class SearchTestCase(TestCase):
|
|||
make_test_data()
|
||||
r = self.client.get("/")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("Search Internet-Drafts" in r.content)
|
||||
self.assertTrue("Search Documents" in r.content)
|
||||
|
||||
def test_drafts_pages(self):
|
||||
draft = make_test_data()
|
||||
|
@ -121,6 +122,32 @@ class SearchTestCase(TestCase):
|
|||
r = self.client.get(urlreverse("index_active_drafts"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(draft.title in r.content)
|
||||
|
||||
def test_ajax_search_docs(self):
|
||||
draft = make_test_data()
|
||||
|
||||
# Document
|
||||
url = urlreverse("ajax_select2_search_docs", kwargs={
|
||||
"model_name": "document",
|
||||
"doc_type": "draft",
|
||||
})
|
||||
r = self.client.get(url, dict(q=draft.name))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
data = json.loads(r.content)
|
||||
self.assertEqual(data[0]["id"], draft.pk)
|
||||
|
||||
# DocAlias
|
||||
doc_alias = draft.docalias_set.get()
|
||||
|
||||
url = urlreverse("ajax_select2_search_docs", kwargs={
|
||||
"model_name": "docalias",
|
||||
"doc_type": "draft",
|
||||
})
|
||||
|
||||
r = self.client.get(url, dict(q=doc_alias.name))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
data = json.loads(r.content)
|
||||
self.assertEqual(data[0]["id"], doc_alias.pk)
|
||||
|
||||
|
||||
class DocTestCase(TestCase):
|
||||
|
@ -373,14 +400,12 @@ class DocTestCase(TestCase):
|
|||
self.client.login(username='iab-chair', password='iab-chair+password')
|
||||
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=doc.name)))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertFalse(q('.actions'))
|
||||
self.assertTrue("Request publication" not in r.content)
|
||||
|
||||
Document.objects.filter(pk=doc.pk).update(stream='iab')
|
||||
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=doc.name)))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue('IESG state' in q('.actions').html())
|
||||
self.assertTrue("Request publication" in r.content)
|
||||
|
||||
|
||||
class AddCommentTestCase(TestCase):
|
||||
|
|
|
@ -174,9 +174,9 @@ class BallotWriteupsTests(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('textarea[name=last_call_text]')), 1)
|
||||
self.assertEqual(len(q('input[type=submit][value*="Save Last Call"]')), 1)
|
||||
# we're secretariat, so we got The Link
|
||||
self.assertEqual(len(q('a:contains("Make Last Call")')), 1)
|
||||
self.assertTrue(q('[type=submit]:contains("Save")'))
|
||||
# we're Secretariat, so we got The Link
|
||||
self.assertEqual(len(q('a:contains("Issue last call")')), 1)
|
||||
|
||||
# subject error
|
||||
r = self.client.post(url, dict(
|
||||
|
@ -184,7 +184,7 @@ class BallotWriteupsTests(TestCase):
|
|||
save_last_call_text="1"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# save
|
||||
r = self.client.post(url, dict(
|
||||
|
@ -243,7 +243,7 @@ class BallotWriteupsTests(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('textarea[name=ballot_writeup]')), 1)
|
||||
self.assertEqual(len(q('input[type=submit][value*="Save Ballot Writeup"]')), 1)
|
||||
self.assertTrue(q('[type=submit]:contains("Save")'))
|
||||
self.assertTrue("IANA does not" in r.content)
|
||||
|
||||
# save
|
||||
|
@ -317,7 +317,7 @@ class BallotWriteupsTests(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('textarea[name=approval_text]')), 1)
|
||||
self.assertEqual(len(q('input[type=submit][value*="Save Approval"]')), 1)
|
||||
self.assertTrue(q('[type=submit]:contains("Save")'))
|
||||
|
||||
# save
|
||||
r = self.client.post(url, dict(
|
||||
|
@ -365,8 +365,8 @@ class ApproveBallotTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue("send out the announcement" in q('.actions input[type=submit]')[0].get('value').lower())
|
||||
self.assertEqual(len(q('.announcement pre:contains("Subject: Protocol Action")')), 1)
|
||||
self.assertTrue(q('[type=submit]:contains("send announcement")'))
|
||||
self.assertEqual(len(q('form pre:contains("Subject: Protocol Action")')), 1)
|
||||
|
||||
# approve
|
||||
mailbox_before = len(outbox)
|
||||
|
@ -466,7 +466,7 @@ class DeferUndeferTestCase(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.defer')),1)
|
||||
self.assertEqual(len(q('[type=submit][value="Defer ballot"]')),1)
|
||||
|
||||
# defer
|
||||
mailbox_before = len(outbox)
|
||||
|
@ -521,7 +521,7 @@ class DeferUndeferTestCase(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.undefer')),1)
|
||||
self.assertEqual(len(q('[type=submit][value="Undefer ballot"]')),1)
|
||||
|
||||
# undefer
|
||||
mailbox_before = len(outbox)
|
||||
|
|
|
@ -71,7 +71,7 @@ class EditCharterTests(TestCase):
|
|||
r = self.client.post(url, dict(charter_state="-12345"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
self.assertEqual(charter.get_state(), first_state)
|
||||
|
||||
# change state
|
||||
|
@ -370,18 +370,16 @@ class EditCharterTests(TestCase):
|
|||
desc="Has been copied",
|
||||
due=due_date,
|
||||
resolved="")
|
||||
# m2 isn't used -- missing test?
|
||||
m2 = GroupMilestone.objects.create(group=group, # pyflakes:ignore
|
||||
state_id="active",
|
||||
desc="To be deleted",
|
||||
due=due_date,
|
||||
resolved="")
|
||||
# m3 isn't used -- missing test?
|
||||
m3 = GroupMilestone.objects.create(group=group, # pyflakes:ignore
|
||||
state_id="charter",
|
||||
desc="Has been copied",
|
||||
due=due_date,
|
||||
resolved="")
|
||||
GroupMilestone.objects.create(group=group,
|
||||
state_id="active",
|
||||
desc="To be deleted",
|
||||
due=due_date,
|
||||
resolved="")
|
||||
GroupMilestone.objects.create(group=group,
|
||||
state_id="charter",
|
||||
desc="Has been copied",
|
||||
due=due_date,
|
||||
resolved="")
|
||||
m4 = GroupMilestone.objects.create(group=group,
|
||||
state_id="charter",
|
||||
desc="New charter milestone",
|
||||
|
@ -392,7 +390,7 @@ class EditCharterTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue("Send out the announcement" in q('input[type=submit]')[0].get('value'))
|
||||
self.assertTrue(q('[type=submit]:contains("Send announcement")'))
|
||||
self.assertEqual(len(q('pre')), 1)
|
||||
|
||||
# approve
|
||||
|
|
|
@ -45,13 +45,13 @@ class ConflictReviewTests(TestCase):
|
|||
r = self.client.post(url,dict(create_in_state=""))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
self.assertEqual(Document.objects.filter(name='conflict-review-imaginary-independent-submission').count() , 0)
|
||||
|
||||
r = self.client.post(url,dict(ad=""))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
self.assertEqual(Document.objects.filter(name='conflict-review-imaginary-independent-submission').count() , 0)
|
||||
|
||||
# successful review start
|
||||
|
@ -139,7 +139,7 @@ class ConflictReviewTests(TestCase):
|
|||
r = self.client.post(url,dict(review_state=""))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# successful change to AD Review
|
||||
adrev_pk = str(State.objects.get(used=True, slug='adrev',type__slug='conflrev').pk)
|
||||
|
@ -274,7 +274,7 @@ class ConflictReviewTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.approve')),1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Send announcement")')), 1)
|
||||
if approve_type == 'appr-noprob':
|
||||
self.assertTrue( 'IESG has no problem' in ''.join(wrap(r.content,2**16)))
|
||||
else:
|
||||
|
|
|
@ -33,7 +33,7 @@ class ChangeStateTests(TestCase):
|
|||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
first_state = draft.get_state("draft-iesg")
|
||||
next_states = first_state.next_states
|
||||
next_states = first_state.next_states.all()
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
|
@ -42,14 +42,14 @@ class ChangeStateTests(TestCase):
|
|||
self.assertEqual(len(q('form select[name=state]')), 1)
|
||||
|
||||
if next_states:
|
||||
self.assertTrue(len(q('.next-states form input[type=hidden]')) > 0)
|
||||
self.assertEqual(len(q('[type=submit][value="%s"]' % next_states[0].name)), 1)
|
||||
|
||||
|
||||
# faulty post
|
||||
r = self.client.post(url, dict(state=State.objects.get(used=True, type="draft", slug="active").pk))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
draft = Document.objects.get(name=draft.name)
|
||||
self.assertEqual(draft.get_state("draft-iesg"), first_state)
|
||||
|
||||
|
@ -81,7 +81,7 @@ class ChangeStateTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('.prev-state form input[name="state"]')), 1)
|
||||
self.assertEqual(len(q('form [type=submit][value="%s"]' % first_state.name)), 1)
|
||||
|
||||
def test_pull_from_rfc_queue(self):
|
||||
draft = make_test_data()
|
||||
|
@ -127,7 +127,7 @@ class ChangeStateTests(TestCase):
|
|||
r = self.client.post(url, dict(state="foobarbaz"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
draft = Document.objects.get(name=draft.name)
|
||||
self.assertEqual(draft.get_state("draft-iana-review"), first_state)
|
||||
|
||||
|
@ -149,7 +149,7 @@ class ChangeStateTests(TestCase):
|
|||
|
||||
self.assertTrue(not draft.latest_event(type="changed_ballot_writeup_text"))
|
||||
r = self.client.post(url, dict(state=State.objects.get(used=True, type="draft-iesg", slug="lc-req").pk))
|
||||
self.assertContains(r, "Your request to issue the Last Call")
|
||||
self.assertTrue("Your request to issue" in r.content)
|
||||
|
||||
# last call text
|
||||
e = draft.latest_event(WriteupDocEvent, type="changed_last_call_text")
|
||||
|
@ -195,7 +195,7 @@ class EditInfoTests(TestCase):
|
|||
r = self.client.post(url, dict(ad="123456789"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
draft = Document.objects.get(name=draft.name)
|
||||
self.assertEqual(draft.ad, prev_ad)
|
||||
|
||||
|
@ -689,7 +689,7 @@ class IndividualInfoFormsTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.change-stream')),1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Save")')), 1)
|
||||
|
||||
# shift to ISE stream
|
||||
messages_before = len(outbox)
|
||||
|
@ -742,13 +742,13 @@ class IndividualInfoFormsTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.change-intended-status')),1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Save")')), 1)
|
||||
|
||||
# don't allow status level to be cleared
|
||||
r = self.client.post(url,dict(intended_std_level=""))
|
||||
self.assertEqual(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# change intended status level
|
||||
messages_before = len(outbox)
|
||||
|
@ -768,7 +768,7 @@ class IndividualInfoFormsTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.telechat-date')),1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Save")')), 1)
|
||||
|
||||
# set a date
|
||||
self.assertFalse(self.doc.latest_event(TelechatDocEvent, "scheduled_for_telechat"))
|
||||
|
@ -791,7 +791,7 @@ class IndividualInfoFormsTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.edit-iesg-note')),1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Save")')),1)
|
||||
|
||||
# post
|
||||
r = self.client.post(url,dict(note='ZpyQFGmA\r\nZpyQFGmA'))
|
||||
|
@ -868,7 +868,7 @@ class IndividualInfoFormsTests(TestCase):
|
|||
r = self.client.post(url, dict(shepherd=two_answers))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
def test_doc_change_shepherd_email(self):
|
||||
self.doc.shepherd = None
|
||||
|
@ -914,15 +914,16 @@ class IndividualInfoFormsTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('span[id=doc_edit_shepherd_writeup]')),1)
|
||||
self.assertEqual(len(q('.content-wrapper a:contains("Edit")')), 1)
|
||||
|
||||
# Try again when no longer a shepherd.
|
||||
|
||||
self.doc.shepherd = None
|
||||
self.doc.save()
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('span[id=doc_edit_shepherd_writeup]')),1)
|
||||
self.assertEqual(len(q('.content-wrapper a:contains("Edit")')), 0)
|
||||
|
||||
def test_doc_change_shepherd_writeup(self):
|
||||
url = urlreverse('doc_edit_shepherd_writeup',kwargs=dict(name=self.docname))
|
||||
|
@ -1037,7 +1038,7 @@ class RequestPublicationTests(TestCase):
|
|||
q = PyQuery(r.content)
|
||||
subject = q('input#id_subject')[0].get("value")
|
||||
self.assertTrue("Document Action" in subject)
|
||||
body = q('.request-publication #id_body').text()
|
||||
body = q('#id_body').text()
|
||||
self.assertTrue("Informational" in body)
|
||||
self.assertTrue("IAB" in body)
|
||||
|
||||
|
@ -1244,38 +1245,34 @@ class ChangeReplacesTests(TestCase):
|
|||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEquals(len(q('form[class=change-replaces]')), 1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Save")')), 1)
|
||||
|
||||
# Post that says replacea replaces base a
|
||||
self.assertEquals(self.basea.get_state().slug,'active')
|
||||
repljson='{"%d":"%s"}'%(DocAlias.objects.get(name=self.basea.name).id,self.basea.name)
|
||||
r = self.client.post(url, dict(replaces=repljson))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEqual(self.basea.get_state().slug,'active')
|
||||
r = self.client.post(url, dict(replaces=str(DocAlias.objects.get(name=self.basea.name).id)))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(RelatedDocument.objects.filter(relationship__slug='replaces',source=self.replacea).count(),1)
|
||||
self.assertEquals(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
||||
|
||||
# Post that says replaceboth replaces both base a and base b
|
||||
url = urlreverse('doc_change_replaces', kwargs=dict(name=self.replaceboth.name))
|
||||
self.assertEquals(self.baseb.get_state().slug,'expired')
|
||||
repljson='{"%d":"%s","%d":"%s"}'%(DocAlias.objects.get(name=self.basea.name).id,self.basea.name,
|
||||
DocAlias.objects.get(name=self.baseb.name).id,self.baseb.name)
|
||||
r = self.client.post(url, dict(replaces=repljson))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
||||
self.assertEquals(Document.objects.get(name='draft-test-base-b').get_state().slug,'repl')
|
||||
self.assertEqual(self.baseb.get_state().slug,'expired')
|
||||
r = self.client.post(url, dict(replaces=str(DocAlias.objects.get(name=self.basea.name).id) + "," + str(DocAlias.objects.get(name=self.baseb.name).id)))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-b').get_state().slug,'repl')
|
||||
|
||||
# Post that undoes replaceboth
|
||||
repljson='{}'
|
||||
r = self.client.post(url, dict(replaces=repljson))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl') # Because A is still also replaced by replacea
|
||||
self.assertEquals(Document.objects.get(name='draft-test-base-b').get_state().slug,'expired')
|
||||
r = self.client.post(url, dict(replaces=""))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl') # Because A is still also replaced by replacea
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-b').get_state().slug,'expired')
|
||||
|
||||
# Post that undoes replacea
|
||||
url = urlreverse('doc_change_replaces', kwargs=dict(name=self.replacea.name))
|
||||
r = self.client.post(url, dict(replaces=repljson))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(Document.objects.get(name='draft-test-base-a').get_state().slug,'active')
|
||||
r = self.client.post(url, dict(replaces=""))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'active')
|
||||
|
||||
|
|
|
@ -41,25 +41,25 @@ class StatusChangeTests(TestCase):
|
|||
r = self.client.post(url,dict(document_name="bogus",title="Bogus Title",ad="",create_in_state=state_strpk,notify='ipu@ietf.org'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
## Must set a name
|
||||
r = self.client.post(url,dict(document_name="",title="Bogus Title",ad=ad_strpk,create_in_state=state_strpk,notify='ipu@ietf.org'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
## Must not choose a document name that already exists
|
||||
r = self.client.post(url,dict(document_name="imaginary-mid-review",title="Bogus Title",ad=ad_strpk,create_in_state=state_strpk,notify='ipu@ietf.org'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
## Must set a title
|
||||
r = self.client.post(url,dict(document_name="bogus",title="",ad=ad_strpk,create_in_state=state_strpk,notify='ipu@ietf.org'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# successful status change start
|
||||
r = self.client.post(url,dict(document_name="imaginary-new",title="A new imaginary status change",ad=ad_strpk,
|
||||
|
@ -90,7 +90,7 @@ class StatusChangeTests(TestCase):
|
|||
r = self.client.post(url,dict(new_state=""))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# successful change to AD Review
|
||||
adrev_pk = str(State.objects.get(slug='adrev',type__slug='statchg').pk)
|
||||
|
@ -283,7 +283,7 @@ class StatusChangeTests(TestCase):
|
|||
messages_before = len(outbox)
|
||||
r = self.client.post(url,dict(last_call_text='stuff',send_last_call_request='Save+and+Request+Last+Call'))
|
||||
self.assertEqual(r.status_code,200)
|
||||
self.assertTrue( 'Last Call Requested' in ''.join(wrap(r.content,2**16)))
|
||||
self.assertTrue( 'Last call requested' in ''.join(wrap(r.content,2**16)))
|
||||
self.assertEqual(len(outbox), messages_before + 1)
|
||||
self.assertTrue('iesg-secretary' in outbox[-1]['To'])
|
||||
self.assertTrue('Last Call:' in outbox[-1]['Subject'])
|
||||
|
@ -307,7 +307,7 @@ class StatusChangeTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.approve')),1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Send announcement")')), 1)
|
||||
# There should be two messages to edit
|
||||
self.assertEqual(q('input#id_form-TOTAL_FORMS').val(),'2')
|
||||
self.assertTrue( '(rfc9999) to Internet Standard' in ''.join(wrap(r.content,2**16)))
|
||||
|
@ -345,30 +345,27 @@ class StatusChangeTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form.edit-status-change-rfcs')),1)
|
||||
self.assertEqual(len(q('.content-wrapper [type=submit]:contains("Save")')),1)
|
||||
# There should be three rows on the form
|
||||
self.assertEqual(len(q('tr[id^=relation_row]')),3)
|
||||
self.assertEqual(len(q('.content-wrapper .row')),3)
|
||||
|
||||
# Try to add a relation to an RFC that doesn't exist
|
||||
r = self.client.post(url,dict(new_relation_row_blah="rfc9997",
|
||||
statchg_relation_row_blah="tois",
|
||||
Submit="Submit"))
|
||||
statchg_relation_row_blah="tois"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
|
||||
# Try to add a relation leaving the relation type blank
|
||||
r = self.client.post(url,dict(new_relation_row_blah="rfc9999",
|
||||
statchg_relation_row_blah="",
|
||||
Submit="Submit"))
|
||||
statchg_relation_row_blah=""))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
|
||||
# Try to add a relation with an unknown relationship type
|
||||
r = self.client.post(url,dict(new_relation_row_blah="rfc9999",
|
||||
statchg_relation_row_blah="badslug",
|
||||
Submit="Submit"))
|
||||
statchg_relation_row_blah="badslug"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
|
@ -379,8 +376,7 @@ class StatusChangeTests(TestCase):
|
|||
new_relation_row_foo="rfc9998",
|
||||
statchg_relation_row_foo="tobcp",
|
||||
new_relation_row_nob="rfc14",
|
||||
statchg_relation_row_nob="tohist",
|
||||
Submit="Submit"))
|
||||
statchg_relation_row_nob="tohist"))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
doc = Document.objects.get(name='status-change-imaginary-mid-review')
|
||||
self.assertEqual(doc.relateddocument_set.count(),3)
|
||||
|
|
|
@ -49,6 +49,7 @@ urlpatterns = patterns('',
|
|||
|
||||
url(r'^all/$', views_search.index_all_drafts, name="index_all_drafts"),
|
||||
url(r'^active/$', views_search.index_active_drafts, name="index_active_drafts"),
|
||||
url(r'^select2search/(?P<model_name>(document|docalias))/(?P<doc_type>draft)/$', views_search.ajax_select2_search_docs, name="ajax_select2_search_docs"),
|
||||
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/(?:(?P<rev>[0-9-]+)/)?$', views_doc.document_main, name="doc_view"),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/history/$', views_doc.document_history, name="doc_history"),
|
||||
|
|
|
@ -116,7 +116,7 @@ def edit_position(request, name, ballot_id):
|
|||
if has_role(request.user, "Secretariat"):
|
||||
ad_id = request.GET.get('ad')
|
||||
if not ad_id:
|
||||
raise Http404()
|
||||
raise Http404
|
||||
ad = get_object_or_404(Person, pk=ad_id)
|
||||
|
||||
old_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, ballot=ballot)
|
||||
|
@ -251,12 +251,12 @@ def send_ballot_comment(request, name, ballot_id):
|
|||
if not has_role(request.user, "Area Director"):
|
||||
ad_id = request.GET.get('ad')
|
||||
if not ad_id:
|
||||
raise Http404()
|
||||
raise Http404
|
||||
ad = get_object_or_404(Person, pk=ad_id)
|
||||
|
||||
pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, ballot=ballot)
|
||||
if not pos:
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
subj = []
|
||||
d = ""
|
||||
|
@ -330,11 +330,11 @@ def defer_ballot(request, name):
|
|||
"""Signal post-pone of ballot, notifying relevant parties."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if doc.type_id not in ('draft','conflrev','statchg'):
|
||||
raise Http404()
|
||||
raise Http404
|
||||
interesting_state = dict(draft='draft-iesg',conflrev='conflrev',statchg='statchg')
|
||||
state = doc.get_state(interesting_state[doc.type_id])
|
||||
if not state or state.slug=='defer' or not doc.telechat_date():
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
telechat_date = TelechatDate.objects.active().order_by("date")[1].date
|
||||
|
@ -380,13 +380,13 @@ def undefer_ballot(request, name):
|
|||
"""undo deferral of ballot ballot."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if doc.type_id not in ('draft','conflrev','statchg'):
|
||||
raise Http404()
|
||||
raise Http404
|
||||
if doc.type_id == 'draft' and not doc.get_state("draft-iesg"):
|
||||
raise Http404()
|
||||
raise Http404
|
||||
interesting_state = dict(draft='draft-iesg',conflrev='conflrev',statchg='statchg')
|
||||
state = doc.get_state(interesting_state[doc.type_id])
|
||||
if not state or state.slug!='defer':
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
telechat_date = TelechatDate.objects.active().order_by("date")[0].date
|
||||
|
||||
|
@ -417,7 +417,7 @@ def lastcalltext(request, name):
|
|||
"""Editing of the last call text"""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.get_state("draft-iesg"):
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
@ -581,7 +581,7 @@ def ballot_approvaltext(request, name):
|
|||
"""Editing of approval text"""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.get_state("draft-iesg"):
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
@ -629,7 +629,7 @@ def approve_ballot(request, name):
|
|||
"""Approve ballot, sending out announcement, changing state."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.get_state("draft-iesg"):
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ from django.contrib.auth.decorators import login_required
|
|||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.doc.models import ( Document, DocHistory, State, DocEvent, BallotDocEvent,
|
||||
BallotPositionDocEvent, InitialReviewDocEvent, NewRevisionDocEvent,
|
||||
BallotPositionDocEvent, InitialReviewDocEvent, NewRevisionDocEvent,
|
||||
WriteupDocEvent, save_document_in_history )
|
||||
from ietf.doc.utils import ( add_state_change_event, close_open_ballots,
|
||||
create_ballot_if_not_open, get_chartering_type )
|
||||
|
@ -33,7 +33,7 @@ from ietf.group.mails import email_iesg_secretary_re_charter
|
|||
class ChangeStateForm(forms.Form):
|
||||
charter_state = forms.ModelChoiceField(State.objects.filter(used=True, type="charter"), label="Charter state", empty_label=None, required=False)
|
||||
initial_time = forms.IntegerField(initial=0, label="Review time", help_text="(in weeks)", required=False)
|
||||
message = forms.CharField(widget=forms.Textarea, help_text="Leave blank to change state without notifying the Secretariat", required=False, label=mark_safe("Message to<br> Secretariat"))
|
||||
message = forms.CharField(widget=forms.Textarea, help_text="Leave blank to change state without notifying the Secretariat", required=False, label=mark_safe("Message to the Secretariat"))
|
||||
comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the charter history", required=False)
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.hide = kwargs.pop('hide', None)
|
||||
|
@ -47,7 +47,7 @@ class ChangeStateForm(forms.Form):
|
|||
# hide requested fields
|
||||
if self.hide:
|
||||
for f in self.hide:
|
||||
self.fields[f].widget = forms.HiddenInput
|
||||
self.fields[f].widget = forms.HiddenInput()
|
||||
|
||||
@login_required
|
||||
def change_state(request, name, option=None):
|
||||
|
@ -101,7 +101,7 @@ def change_state(request, name, option=None):
|
|||
e.state_id = group.state.slug
|
||||
e.desc = "Group state changed to %s from %s" % (group.state, oldstate)
|
||||
e.save()
|
||||
|
||||
|
||||
else:
|
||||
charter_state = State.objects.get(used=True, type="charter", slug="approved")
|
||||
charter_rev = approved_revision(charter.rev)
|
||||
|
@ -382,12 +382,12 @@ def submit(request, name=None, option=None):
|
|||
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), charter.rev)
|
||||
e.rev = charter.rev
|
||||
e.save()
|
||||
|
||||
|
||||
# Save file on disk
|
||||
form.save(group, charter.rev)
|
||||
|
||||
if option in ['initcharter','recharter'] and charter.ad == None:
|
||||
charter.ad = group.ad
|
||||
charter.ad = group.ad
|
||||
|
||||
charter.time = datetime.datetime.now()
|
||||
charter.save()
|
||||
|
@ -460,7 +460,7 @@ def announcement_text(request, name, ann):
|
|||
e.desc = "%s %s text was changed" % (group.type.name, ann)
|
||||
e.text = t
|
||||
e.save()
|
||||
|
||||
|
||||
charter.time = e.time
|
||||
charter.save()
|
||||
|
||||
|
@ -495,7 +495,7 @@ class BallotWriteupForm(forms.Form):
|
|||
|
||||
def clean_ballot_writeup(self):
|
||||
return self.cleaned_data["ballot_writeup"].replace("\r", "")
|
||||
|
||||
|
||||
@role_required('Area Director','Secretariat')
|
||||
def ballot_writeupnotes(request, name):
|
||||
"""Editing of ballot write-up and notes"""
|
||||
|
@ -508,13 +508,13 @@ def ballot_writeupnotes(request, name):
|
|||
login = request.user.person
|
||||
|
||||
approval = charter.latest_event(WriteupDocEvent, type="changed_action_announcement")
|
||||
|
||||
|
||||
existing = charter.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
|
||||
if not existing:
|
||||
existing = generate_ballot_writeup(request, charter)
|
||||
|
||||
reissue = charter.latest_event(DocEvent, type="sent_ballot_announcement")
|
||||
|
||||
|
||||
form = BallotWriteupForm(initial=dict(ballot_writeup=existing.text))
|
||||
|
||||
if request.method == 'POST' and ("save_ballot_writeup" in request.POST or "send_ballot" in request.POST):
|
||||
|
@ -699,7 +699,7 @@ def approve(request, name):
|
|||
send_mail_preformatted(request, announcement)
|
||||
|
||||
return HttpResponseRedirect(charter.get_absolute_url())
|
||||
|
||||
|
||||
return render_to_response('doc/charter/approve.html',
|
||||
dict(charter=charter,
|
||||
announcement=announcement),
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
import os, datetime, urllib, json, glob
|
||||
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.shortcuts import render_to_response, get_object_or_404, redirect
|
||||
from django.shortcuts import render_to_response, get_object_or_404, redirect, render
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
@ -869,20 +869,17 @@ def telechat_date(request, name):
|
|||
e = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
initial_returning_item = bool(e and e.returning_item)
|
||||
|
||||
prompts = []
|
||||
warnings = []
|
||||
if e and e.telechat_date and doc.type.slug != 'charter':
|
||||
if e.telechat_date==datetime.date.today():
|
||||
prompts.append( "This document is currently scheduled for today's telechat. "
|
||||
+"Please set the returning item bit carefully.")
|
||||
warnings.append( "This document is currently scheduled for today's telechat. "
|
||||
+"Please set the returning item bit carefully.")
|
||||
|
||||
elif e.telechat_date<datetime.date.today() and has_same_ballot(doc,e.telechat_date):
|
||||
initial_returning_item = True
|
||||
prompts.append( "This document appears to have been on a previous telechat with the same ballot, "
|
||||
warnings.append( "This document appears to have been on a previous telechat with the same ballot, "
|
||||
+"so the returning item bit has been set. Clear it if that is not appropriate.")
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
initial = dict(telechat_date=e.telechat_date if e else None,
|
||||
returning_item = initial_returning_item,
|
||||
)
|
||||
|
@ -901,13 +898,12 @@ def telechat_date(request, name):
|
|||
if doc.type.slug=='charter':
|
||||
del form.fields['returning_item']
|
||||
|
||||
return render_to_response('doc/edit_telechat_date.html',
|
||||
return render(request, 'doc/edit_telechat_date.html',
|
||||
dict(doc=doc,
|
||||
form=form,
|
||||
user=request.user,
|
||||
prompts=prompts,
|
||||
login=login),
|
||||
context_instance=RequestContext(request))
|
||||
warnings=warnings,
|
||||
login=login))
|
||||
|
||||
@role_required('Area Director', 'Secretariat')
|
||||
def edit_notify(request, name):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# changing state and metadata on Internet Drafts
|
||||
|
||||
import datetime, json
|
||||
import datetime
|
||||
|
||||
from django import forms
|
||||
from django.http import HttpResponseRedirect, HttpResponseForbidden, Http404
|
||||
|
@ -24,13 +24,14 @@ from ietf.doc.utils import ( add_state_change_event, can_adopt_draft,
|
|||
get_tags_for_stream_id, nice_consensus,
|
||||
update_reminder, update_telechat, make_notify_changed_event, get_initial_notify )
|
||||
from ietf.doc.lastcall import request_last_call
|
||||
from ietf.doc.fields import SearchableDocAliasesField
|
||||
from ietf.group.models import Group, Role
|
||||
from ietf.iesg.models import TelechatDate
|
||||
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person
|
||||
from ietf.ietfauth.utils import role_required
|
||||
from ietf.message.models import Message
|
||||
from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName
|
||||
from ietf.person.fields import AutocompletedEmailField
|
||||
from ietf.person.fields import SearchableEmailField
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.secr.lib.template import jsonapi
|
||||
from ietf.utils.mail import send_mail, send_mail_message
|
||||
|
@ -64,7 +65,7 @@ def change_state(request, name):
|
|||
and logging the change as a comment."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if (not doc.latest_event(type="started_iesg_process")) or doc.get_state_slug() == "expired":
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
@ -214,7 +215,7 @@ def change_stream(request, name):
|
|||
and logging the change as a comment."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if not doc.type_id=='draft':
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
if not (has_role(request.user, ("Area Director", "Secretariat")) or
|
||||
(request.user.is_authenticated() and
|
||||
|
@ -307,35 +308,21 @@ def collect_email_addresses(emails, doc):
|
|||
return emails
|
||||
|
||||
class ReplacesForm(forms.Form):
|
||||
replaces = forms.CharField(max_length=512,widget=forms.HiddenInput)
|
||||
replaces = SearchableDocAliasesField(required=False)
|
||||
comment = forms.CharField(widget=forms.Textarea, required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.doc = kwargs.pop('doc')
|
||||
super(ReplacesForm, self).__init__(*args, **kwargs)
|
||||
drafts = {}
|
||||
for d in self.doc.related_that_doc("replaces"):
|
||||
drafts[d.id] = d.document.name
|
||||
self.initial['replaces'] = json.dumps(drafts)
|
||||
self.initial['replaces'] = self.doc.related_that_doc("replaces")
|
||||
|
||||
def clean_replaces(self):
|
||||
data = self.cleaned_data['replaces'].strip()
|
||||
if data:
|
||||
ids = [int(x) for x in json.loads(data)]
|
||||
else:
|
||||
return []
|
||||
objects = []
|
||||
for id in ids:
|
||||
try:
|
||||
d = DocAlias.objects.get(pk=id)
|
||||
except DocAlias.DoesNotExist:
|
||||
raise forms.ValidationError("ERROR: %s not found for id %d" % DocAlias._meta.verbos_name, id)
|
||||
for d in self.cleaned_data['replaces']:
|
||||
if d.document == self.doc:
|
||||
raise forms.ValidationError("ERROR: A draft can't replace itself")
|
||||
raise forms.ValidationError("A draft can't replace itself")
|
||||
if d.document.type_id == "draft" and d.document.get_state_slug() == "rfc":
|
||||
raise forms.ValidationError("ERROR: A draft can't replace an RFC")
|
||||
objects.append(d)
|
||||
return objects
|
||||
raise forms.ValidationError("A draft can't replace an RFC")
|
||||
return self.cleaned_data['replaces']
|
||||
|
||||
def replaces(request, name):
|
||||
"""Change 'replaces' set of a Document of type 'draft' , notifying parties
|
||||
|
@ -505,10 +492,10 @@ def to_iesg(request,name):
|
|||
doc = get_object_or_404(Document, docalias__name=name, stream='ietf')
|
||||
|
||||
if doc.get_state_slug('draft') == "expired" or doc.get_state_slug('draft-iesg') == 'pub-req' :
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
if not is_authorized_in_doc_stream(request.user, doc):
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
target_state={
|
||||
'iesg' : State.objects.get(type='draft-iesg',slug='pub-req'),
|
||||
|
@ -614,7 +601,7 @@ def edit_info(request, name):
|
|||
necessary and logging changes as document events."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if doc.get_state_slug() == "expired":
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
@ -764,7 +751,7 @@ def request_resurrect(request, name):
|
|||
"""Request resurrect of expired Internet Draft."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if doc.get_state_slug() != "expired":
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
@ -788,7 +775,7 @@ def resurrect(request, name):
|
|||
"""Resurrect expired Internet Draft."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if doc.get_state_slug() != "expired":
|
||||
raise Http404()
|
||||
raise Http404
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
@ -942,7 +929,7 @@ def edit_shepherd_writeup(request, name):
|
|||
context_instance=RequestContext(request))
|
||||
|
||||
class ShepherdForm(forms.Form):
|
||||
shepherd = AutocompletedEmailField(required=False, only_users=True)
|
||||
shepherd = SearchableEmailField(required=False, only_users=True)
|
||||
|
||||
def edit_shepherd(request, name):
|
||||
"""Change the shepherd for a Document"""
|
||||
|
|
|
@ -37,7 +37,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
|||
from django.shortcuts import render_to_response
|
||||
from django.db.models import Q
|
||||
from django.template import RequestContext
|
||||
from django.http import Http404, HttpResponseBadRequest
|
||||
from django.http import Http404, HttpResponseBadRequest, HttpResponse
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -45,6 +45,7 @@ from ietf.community.models import CommunityList
|
|||
from ietf.doc.models import ( Document, DocAlias, State, RelatedDocument, DocEvent,
|
||||
LastCallDocEvent, TelechatDocEvent, IESG_SUBSTATE_TAGS )
|
||||
from ietf.doc.expire import expirable_draft
|
||||
from ietf.doc.fields import select2_id_doc_name_json
|
||||
from ietf.group.models import Group
|
||||
from ietf.idindex.index import active_drafts_index_by_group
|
||||
from ietf.ipr.models import IprDocAlias
|
||||
|
@ -52,6 +53,7 @@ from ietf.name.models import DocTagName, DocTypeName, StreamName
|
|||
from ietf.person.models import Person
|
||||
from ietf.utils.draft_search import normalize_draftname
|
||||
|
||||
|
||||
class SearchForm(forms.Form):
|
||||
name = forms.CharField(required=False)
|
||||
rfcs = forms.BooleanField(required=False, initial=True)
|
||||
|
@ -148,7 +150,7 @@ def fill_in_search_attributes(docs):
|
|||
for d in docs:
|
||||
if isinstance(d,DocAlias):
|
||||
d = d.document
|
||||
rel_this_doc = d.all_related_that_doc(['replaces','obs'])
|
||||
rel_this_doc = d.all_related_that_doc(['replaces','obs'])
|
||||
for rel in rel_this_doc:
|
||||
rel_id_camefrom.setdefault(rel.document.pk,[]).append(d.pk)
|
||||
rel_docs += [x.document for x in rel_this_doc]
|
||||
|
@ -240,7 +242,7 @@ def retrieve_search_results(form, all_types=False):
|
|||
"""Takes a validated SearchForm and return the results."""
|
||||
if not form.is_valid():
|
||||
raise ValueError("SearchForm doesn't validate: %s" % form.errors)
|
||||
|
||||
|
||||
query = form.cleaned_data
|
||||
|
||||
types=[];
|
||||
|
@ -282,7 +284,7 @@ def retrieve_search_results(form, all_types=False):
|
|||
if query["olddrafts"]:
|
||||
allowed_draft_states.extend(['repl', 'expired', 'auth-rm', 'ietf-rm'])
|
||||
|
||||
docs = docs.filter(Q(states__slug__in=allowed_draft_states) |
|
||||
docs = docs.filter(Q(states__slug__in=allowed_draft_states) |
|
||||
~Q(type__slug='draft')).distinct()
|
||||
|
||||
# radio choices
|
||||
|
@ -366,7 +368,7 @@ def retrieve_search_results(form, all_types=False):
|
|||
{'title': 'Document', 'key':'document'},
|
||||
{'title': 'Title', 'key':'title'},
|
||||
{'title': 'Date', 'key':'date'},
|
||||
{'title': 'Status', 'key':'status', 'colspan':'2'},
|
||||
{'title': 'Status', 'key':'status'},
|
||||
{'title': 'IPR', 'key':'ipr'},
|
||||
{'title': 'AD / Shepherd', 'key':'ad'}]
|
||||
|
||||
|
@ -441,14 +443,14 @@ def ad_dashboard_group(doc):
|
|||
return '%s Internet-Draft' % doc.get_state('draft').name
|
||||
elif doc.type.slug=='conflrev':
|
||||
if doc.get_state_slug('conflrev') in ('appr-reqnopub-sent','appr-noprob-sent'):
|
||||
return 'Approved Conflict Review'
|
||||
return 'Approved Conflict Review'
|
||||
elif doc.get_state_slug('conflrev') in ('appr-reqnopub-pend','appr-noprob-pend','appr-reqnopub-pr','appr-noprob-pr'):
|
||||
return "%s Conflict Review" % State.objects.get(type__slug='draft-iesg',slug='approved')
|
||||
else:
|
||||
return '%s Conflict Review' % doc.get_state('conflrev')
|
||||
elif doc.type.slug=='statchg':
|
||||
if doc.get_state_slug('statchg') in ('appr-sent',):
|
||||
return 'Approved Status Change'
|
||||
return 'Approved Status Change'
|
||||
if doc.get_state_slug('statchg') in ('appr-pend','appr-pr'):
|
||||
return '%s Status Change' % State.objects.get(type__slug='draft-iesg',slug='approved')
|
||||
else:
|
||||
|
@ -462,7 +464,7 @@ def ad_dashboard_group(doc):
|
|||
return "Document"
|
||||
|
||||
def ad_dashboard_sort_key(doc):
|
||||
|
||||
|
||||
if doc.type.slug=='draft' and doc.get_state_slug('draft') == 'rfc':
|
||||
return "21%04d" % int(doc.rfc_number())
|
||||
if doc.type.slug=='statchg' and doc.get_state_slug('statchg') == 'appr-sent':
|
||||
|
@ -475,26 +477,26 @@ def ad_dashboard_sort_key(doc):
|
|||
seed = ad_dashboard_group(doc)
|
||||
|
||||
if doc.type.slug=='conflrev' and doc.get_state_slug('conflrev') == 'adrev':
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='ad-eval')
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='ad-eval')
|
||||
return "1%d%s" % (state.order,seed)
|
||||
|
||||
if doc.type.slug=='charter':
|
||||
if doc.get_state_slug('charter') in ('notrev','infrev'):
|
||||
return "100%s" % seed
|
||||
elif doc.get_state_slug('charter') == 'intrev':
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='ad-eval')
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='ad-eval')
|
||||
return "1%d%s" % (state.order,seed)
|
||||
elif doc.get_state_slug('charter') == 'extrev':
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='lc')
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='lc')
|
||||
return "1%d%s" % (state.order,seed)
|
||||
elif doc.get_state_slug('charter') == 'iesgrev':
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='iesg-eva')
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='iesg-eva')
|
||||
return "1%d%s" % (state.order,seed)
|
||||
|
||||
if doc.type.slug=='statchg' and doc.get_state_slug('statchg') == 'adrev':
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='ad-eval')
|
||||
state = State.objects.get(type__slug='draft-iesg',slug='ad-eval')
|
||||
return "1%d%s" % (state.order,seed)
|
||||
|
||||
|
||||
if seed.startswith('Needs Shepherd'):
|
||||
return "100%s" % seed
|
||||
if seed.endswith(' Document'):
|
||||
|
@ -627,3 +629,28 @@ def index_active_drafts(request):
|
|||
groups = active_drafts_index_by_group()
|
||||
|
||||
return render_to_response("doc/index_active_drafts.html", { 'groups': groups }, context_instance=RequestContext(request))
|
||||
|
||||
def ajax_select2_search_docs(request, model_name, doc_type):
|
||||
if model_name == "docalias":
|
||||
model = DocAlias
|
||||
else:
|
||||
model = Document
|
||||
|
||||
q = [w.strip() for w in request.GET.get('q', '').split() if w.strip()]
|
||||
|
||||
if not q:
|
||||
objs = model.objects.none()
|
||||
else:
|
||||
qs = model.objects.all()
|
||||
|
||||
if model == Document:
|
||||
qs = qs.filter(type=doc_type)
|
||||
elif model == DocAlias:
|
||||
qs = qs.filter(document__type=doc_type)
|
||||
|
||||
for t in q:
|
||||
qs = qs.filter(name__icontains=t)
|
||||
|
||||
objs = qs.distinct().order_by("name")[:20]
|
||||
|
||||
return HttpResponse(select2_id_doc_name_json(objs), content_type='application/json')
|
||||
|
|
|
@ -399,11 +399,11 @@ def clean_helper(form, formtype):
|
|||
elif k.startswith('statchg_relation_row'):
|
||||
status_fields[k[21:]]=v
|
||||
for key in rfc_fields:
|
||||
if rfc_fields[key]!="":
|
||||
if key in status_fields:
|
||||
new_relations[rfc_fields[key]]=status_fields[key]
|
||||
else:
|
||||
new_relations[rfc_fields[key]]=None
|
||||
if rfc_fields[key]!="":
|
||||
if key in status_fields:
|
||||
new_relations[rfc_fields[key]]=status_fields[key]
|
||||
else:
|
||||
new_relations[rfc_fields[key]]=None
|
||||
|
||||
form.relations = new_relations
|
||||
|
||||
|
@ -568,7 +568,7 @@ def edit_relations(request, name):
|
|||
|
||||
if request.method == 'POST':
|
||||
form = EditStatusChangeForm(request.POST)
|
||||
if 'Submit' in request.POST and form.is_valid():
|
||||
if form.is_valid():
|
||||
|
||||
old_relations={}
|
||||
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
||||
|
@ -590,9 +590,6 @@ def edit_relations(request, name):
|
|||
|
||||
return HttpResponseRedirect(status_change.get_absolute_url())
|
||||
|
||||
elif 'Cancel' in request.POST:
|
||||
return HttpResponseRedirect(status_change.get_absolute_url())
|
||||
|
||||
else:
|
||||
relations={}
|
||||
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
||||
|
|
|
@ -19,9 +19,10 @@ from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStat
|
|||
from ietf.group.utils import save_group_in_history, can_manage_group_type
|
||||
from ietf.group.utils import get_group_or_404
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.person.fields import AutocompletedEmailsField
|
||||
from ietf.person.fields import SearchableEmailsField
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.group.mails import email_iesg_secretary_re_charter, email_iesg_secretary_personnel_change
|
||||
from ietf.utils.ordereddict import insert_after_in_ordered_dict
|
||||
|
||||
MAX_GROUP_DELEGATES = 3
|
||||
|
||||
|
@ -29,21 +30,20 @@ class GroupForm(forms.Form):
|
|||
name = forms.CharField(max_length=255, label="Name", required=True)
|
||||
acronym = forms.CharField(max_length=10, label="Acronym", required=True)
|
||||
state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True)
|
||||
chairs = AutocompletedEmailsField(required=False, only_users=True)
|
||||
secretaries = AutocompletedEmailsField(required=False, only_users=True)
|
||||
techadv = AutocompletedEmailsField(label="Technical Advisors", required=False, only_users=True)
|
||||
delegates = AutocompletedEmailsField(required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES,
|
||||
help_text=mark_safe("Chairs can delegate the authority to update the state of group documents - max %s persons at a given time" % MAX_GROUP_DELEGATES))
|
||||
chairs = SearchableEmailsField(label="Chairs", required=False, only_users=True)
|
||||
secretaries = SearchableEmailsField(label="Secretarias", required=False, only_users=True)
|
||||
techadv = SearchableEmailsField(label="Technical Advisors", required=False, only_users=True)
|
||||
delegates = SearchableEmailsField(label="Delegates", required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES,
|
||||
help_text=mark_safe("Chairs can delegate the authority to update the state of group documents - at most %s persons at a given time." % MAX_GROUP_DELEGATES))
|
||||
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'), label="Shepherding AD", empty_label="(None)", required=False)
|
||||
parent = forms.ModelChoiceField(Group.objects.filter(state="active").order_by('name'), empty_label="(None)", 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/path (Optional description). Separate multiple entries with newline.", required=False)
|
||||
urls = forms.CharField(widget=forms.Textarea, label="Additional URLs", help_text="Format: https://site/path (Optional description). Separate multiple entries with newline. Prefer HTTPS URLs where possible.", required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.group = kwargs.pop('group', None)
|
||||
self.confirmed = kwargs.pop('confirmed', False)
|
||||
self.group_type = kwargs.pop('group_type', False)
|
||||
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
@ -57,10 +57,8 @@ class GroupForm(forms.Form):
|
|||
if ad_pk and ad_pk not in [pk for pk, name in choices]:
|
||||
self.fields['ad'].choices = list(choices) + [("", "-------"), (ad_pk, Person.objects.get(pk=ad_pk).plain_name())]
|
||||
|
||||
self.confirm_msg = ""
|
||||
self.autoenable_confirm = False
|
||||
if self.group:
|
||||
self.fields['acronym'].widget.attrs['readonly'] = True
|
||||
self.fields['acronym'].widget.attrs['readonly'] = ""
|
||||
|
||||
if self.group_type == "rg":
|
||||
self.fields['ad'].widget = forms.HiddenInput()
|
||||
|
@ -71,9 +69,6 @@ class GroupForm(forms.Form):
|
|||
self.fields['parent'].label = "IETF Area"
|
||||
|
||||
def clean_acronym(self):
|
||||
self.confirm_msg = ""
|
||||
self.autoenable_confirm = False
|
||||
|
||||
# Changing the acronym of an already existing group will cause 404s all
|
||||
# over the place, loose history, and generally muck up a lot of
|
||||
# things, so we don't permit it
|
||||
|
@ -90,27 +85,41 @@ class GroupForm(forms.Form):
|
|||
if existing:
|
||||
existing = existing[0]
|
||||
|
||||
if existing and existing.type_id == self.group_type:
|
||||
if self.confirmed:
|
||||
return acronym # take over confirmed
|
||||
confirmed = self.data.get("confirm_acronym", False)
|
||||
|
||||
def insert_confirm_field(label, initial):
|
||||
# set required to false, we don't need it since we do the
|
||||
# validation of the field in here, and otherwise the
|
||||
# browser and Django may barf
|
||||
insert_after_in_ordered_dict(self.fields, "confirm_acronym", forms.BooleanField(label=label, required=False), after="acronym")
|
||||
# we can't set initial, it's ignored since the form is bound, instead mutate the data
|
||||
self.data = self.data.copy()
|
||||
self.data["confirm_acronym"] = initial
|
||||
|
||||
if existing and existing.type_id == self.group_type:
|
||||
if existing.state_id == "bof":
|
||||
self.confirm_msg = "Turn BoF %s into proposed %s and start chartering it" % (existing.acronym, existing.type.name)
|
||||
self.autoenable_confirm = True
|
||||
raise forms.ValidationError("Warning: Acronym used for an existing BoF (%s)." % existing.name)
|
||||
insert_confirm_field(label="Turn BoF %s into proposed %s and start chartering it" % (existing.acronym, existing.type.name), initial=True)
|
||||
if confirmed:
|
||||
return acronym
|
||||
else:
|
||||
raise forms.ValidationError("Warning: Acronym used for an existing BoF (%s)." % existing.name)
|
||||
else:
|
||||
self.confirm_msg = "Set state of %s %s to proposed and start chartering it" % (existing.acronym, existing.type.name)
|
||||
self.autoenable_confirm = False
|
||||
raise forms.ValidationError("Warning: Acronym used for an existing %s (%s, %s)." % (existing.type.name, existing.name, existing.state.name if existing.state else "unknown state"))
|
||||
insert_confirm_field(label="Set state of %s %s to proposed and start chartering it" % (existing.acronym, existing.type.name), initial=False)
|
||||
if confirmed:
|
||||
return acronym
|
||||
else:
|
||||
raise forms.ValidationError("Warning: Acronym used for an existing %s (%s, %s)." % (existing.type.name, existing.name, existing.state.name if existing.state else "unknown state"))
|
||||
|
||||
if existing:
|
||||
raise forms.ValidationError("Acronym used for an existing group (%s)." % existing.name)
|
||||
|
||||
old = GroupHistory.objects.filter(acronym__iexact=acronym, type__in=("wg", "rg"))
|
||||
if old and not self.confirmed:
|
||||
self.confirm_msg = "Confirm reusing acronym %s" % old[0].acronym
|
||||
self.autoenable_confirm = False
|
||||
raise forms.ValidationError("Warning: Acronym used for a historic group.")
|
||||
if old:
|
||||
insert_confirm_field(label="Confirm reusing acronym %s" % old[0].acronym, initial=False)
|
||||
if confirmed:
|
||||
return acronym
|
||||
else:
|
||||
raise forms.ValidationError("Warning: Acronym used for a historic group.")
|
||||
|
||||
return acronym
|
||||
|
||||
|
@ -149,7 +158,7 @@ def get_or_create_initial_charter(group, group_type):
|
|||
)
|
||||
charter.save()
|
||||
charter.set_state(State.objects.get(used=True, type="charter", slug="notrev"))
|
||||
|
||||
|
||||
# Create an alias as well
|
||||
DocAlias.objects.create(name=charter.name, document=charter)
|
||||
|
||||
|
@ -190,7 +199,7 @@ def edit(request, group_type=None, acronym=None, action="edit"):
|
|||
group_type = group.type_id
|
||||
|
||||
if request.method == 'POST':
|
||||
form = GroupForm(request.POST, group=group, confirmed=request.POST.get("confirmed", False), group_type=group_type)
|
||||
form = GroupForm(request.POST, group=group, group_type=group_type)
|
||||
if form.is_valid():
|
||||
clean = form.cleaned_data
|
||||
if new_group:
|
||||
|
@ -220,12 +229,12 @@ def edit(request, group_type=None, acronym=None, action="edit"):
|
|||
group.charter = get_or_create_initial_charter(group, group_type)
|
||||
|
||||
changes = []
|
||||
|
||||
|
||||
def desc(attr, new, old):
|
||||
entry = "%(attr)s changed to <b>%(new)s</b> from %(old)s"
|
||||
if new_group:
|
||||
entry = "%(attr)s changed to <b>%(new)s</b>"
|
||||
|
||||
|
||||
return entry % dict(attr=attr, new=new, old=old)
|
||||
|
||||
def diff(attr, name):
|
||||
|
|
|
@ -304,7 +304,7 @@ def construct_group_menu_context(request, group, selected, group_type, others):
|
|||
|
||||
if group.features.has_milestones:
|
||||
if group.state_id != "proposed" and (is_chair or can_manage):
|
||||
actions.append((u"Add or edit milestones", urlreverse("group_edit_milestones", kwargs=kwargs)))
|
||||
actions.append((u"Edit milestones", urlreverse("group_edit_milestones", kwargs=kwargs)))
|
||||
|
||||
if group.features.has_materials and can_manage_materials(request.user, group):
|
||||
actions.append((u"Upload material", urlreverse("ietf.doc.views_material.choose_material_type", kwargs=kwargs)))
|
||||
|
|
|
@ -2,102 +2,72 @@
|
|||
|
||||
import datetime
|
||||
import calendar
|
||||
import json
|
||||
|
||||
from django import forms
|
||||
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseRedirect, Http404
|
||||
from django.http import HttpResponseForbidden, HttpResponseBadRequest, HttpResponseRedirect, Http404
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from ietf.doc.models import Document, DocEvent
|
||||
from ietf.doc.models import DocEvent
|
||||
from ietf.doc.utils import get_chartering_type
|
||||
from ietf.doc.fields import SearchableDocumentsField
|
||||
from ietf.group.models import GroupMilestone, MilestoneGroupEvent
|
||||
from ietf.group.utils import (save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type,
|
||||
get_group_or_404)
|
||||
from ietf.name.models import GroupMilestoneStateName
|
||||
from ietf.group.mails import email_milestones_changed
|
||||
|
||||
def json_doc_names(docs):
|
||||
return json.dumps([{"id": doc.pk, "name": doc.name } for doc in docs])
|
||||
|
||||
def parse_doc_names(s):
|
||||
return Document.objects.filter(pk__in=[x.strip() for x in s.split(",") if x.strip()], type="draft")
|
||||
from ietf.utils.fields import DatepickerDateField
|
||||
|
||||
class MilestoneForm(forms.Form):
|
||||
id = forms.IntegerField(required=True, widget=forms.HiddenInput)
|
||||
|
||||
desc = forms.CharField(max_length=500, label="Milestone:", required=True)
|
||||
due_month = forms.TypedChoiceField(choices=(), required=True, coerce=int)
|
||||
due_year = forms.TypedChoiceField(choices=(), required=True, coerce=int)
|
||||
desc = forms.CharField(max_length=500, label="Milestone", required=True)
|
||||
due = DatepickerDateField(date_format="MM yyyy", picker_settings={"min-view-mode": "months", "autoclose": "1", "view-mode": "years" }, required=True)
|
||||
docs = SearchableDocumentsField(label="Drafts", required=False, help_text="Any drafts that the milestone concerns.")
|
||||
resolved_checkbox = forms.BooleanField(required=False, label="Resolved")
|
||||
resolved = forms.CharField(max_length=50, required=False)
|
||||
resolved = forms.CharField(label="Resolved as", max_length=50, required=False)
|
||||
|
||||
delete = forms.BooleanField(required=False, initial=False)
|
||||
|
||||
docs = forms.CharField(max_length=10000, required=False)
|
||||
|
||||
accept = forms.ChoiceField(choices=(("accept", "Accept"), ("reject", "Reject and delete"), ("noaction", "No action")),
|
||||
review = forms.ChoiceField(label="Review action", help_text="Choose whether to accept or reject the proposed changes.",
|
||||
choices=(("accept", "Accept"), ("reject", "Reject and delete"), ("noaction", "No action")),
|
||||
required=False, initial="noaction", widget=forms.RadioSelect)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["label_suffix"] = ""
|
||||
|
||||
def __init__(self, needs_review, reviewer, *args, **kwargs):
|
||||
m = self.milestone = kwargs.pop("instance", None)
|
||||
|
||||
self.needs_review = kwargs.pop("needs_review", False)
|
||||
can_review = not self.needs_review
|
||||
can_review = not needs_review
|
||||
|
||||
if m:
|
||||
self.needs_review = m.state_id == "review"
|
||||
needs_review = m.state_id == "review"
|
||||
|
||||
if not "initial" in kwargs:
|
||||
kwargs["initial"] = {}
|
||||
kwargs["initial"].update(dict(id=m.pk,
|
||||
desc=m.desc,
|
||||
due_month=m.due.month,
|
||||
due_year=m.due.year,
|
||||
due=m.due,
|
||||
resolved_checkbox=bool(m.resolved),
|
||||
resolved=m.resolved,
|
||||
docs=",".join(m.docs.values_list("pk", flat=True)),
|
||||
docs=m.docs.all(),
|
||||
delete=False,
|
||||
accept="noaction" if can_review and self.needs_review else None,
|
||||
review="noaction" if can_review and needs_review else "",
|
||||
))
|
||||
|
||||
kwargs["prefix"] = "m%s" % m.pk
|
||||
|
||||
super(MilestoneForm, self).__init__(*args, **kwargs)
|
||||
|
||||
# set choices for due date
|
||||
this_year = datetime.date.today().year
|
||||
self.fields["resolved"].widget.attrs["data-default"] = "Done"
|
||||
|
||||
self.fields["due_month"].choices = [(month, datetime.date(this_year, month, 1).strftime("%B")) for month in range(1, 13)]
|
||||
if needs_review and self.milestone and self.milestone.state_id != "review":
|
||||
self.fields["desc"].widget.attrs["readonly"] = True
|
||||
|
||||
years = [ y for y in range(this_year, this_year + 10)]
|
||||
self.changed = False
|
||||
|
||||
initial = self.initial.get("due_year")
|
||||
if initial and initial not in years:
|
||||
years.insert(0, initial)
|
||||
if not (needs_review and can_review):
|
||||
self.fields["review"].widget = forms.HiddenInput()
|
||||
|
||||
self.fields["due_year"].choices = zip(years, map(str, years))
|
||||
|
||||
# figure out what to prepopulate many-to-many field with
|
||||
pre = ""
|
||||
if not self.is_bound:
|
||||
pre = self.initial.get("docs", "")
|
||||
else:
|
||||
pre = self["docs"].data or ""
|
||||
|
||||
# this is ugly, but putting it on self["docs"] is buggy with a
|
||||
# bound/unbound form in Django 1.2
|
||||
self.docs_names = parse_doc_names(pre)
|
||||
self.docs_prepopulate = json_doc_names(self.docs_names)
|
||||
|
||||
# calculate whether we've changed
|
||||
self.changed = self.is_bound and (not self.milestone or any(unicode(self[f].data) != unicode(self.initial[f]) for f in self.fields.iterkeys()))
|
||||
|
||||
def clean_docs(self):
|
||||
s = self.cleaned_data["docs"]
|
||||
return Document.objects.filter(pk__in=[x.strip() for x in s.split(",") if x.strip()], type="draft")
|
||||
self.needs_review = needs_review
|
||||
|
||||
def clean_resolved(self):
|
||||
r = self.cleaned_data["resolved"].strip()
|
||||
|
@ -137,14 +107,17 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
|||
title = "Edit charter milestones for %s %s" % (group.acronym, group.type.name)
|
||||
milestones = group.groupmilestone_set.filter(state="charter")
|
||||
|
||||
reviewer = milestone_reviewer_for_group_type(group_type)
|
||||
|
||||
forms = []
|
||||
|
||||
milestones_dict = dict((str(m.id), m) for m in milestones)
|
||||
|
||||
def due_month_year_to_date(c):
|
||||
y = c["due_year"]
|
||||
m = c["due_month"]
|
||||
return datetime.date(y, m, calendar.monthrange(y, m)[1])
|
||||
y = c["due"].year
|
||||
m = c["due"].month
|
||||
first_day, last_day = calendar.monthrange(y, m)
|
||||
return datetime.date(y, m, last_day)
|
||||
|
||||
def set_attributes_from_form(f, m):
|
||||
c = f.cleaned_data
|
||||
|
@ -156,10 +129,24 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
|||
m.state = GroupMilestoneStateName.objects.get(slug="active")
|
||||
elif milestone_set == "charter":
|
||||
m.state = GroupMilestoneStateName.objects.get(slug="charter")
|
||||
|
||||
m.desc = c["desc"]
|
||||
m.due = due_month_year_to_date(c)
|
||||
m.resolved = c["resolved"]
|
||||
|
||||
def milestone_changed(f, m):
|
||||
# we assume that validation has run
|
||||
if not m or not f.is_valid():
|
||||
return True
|
||||
|
||||
c = f.cleaned_data
|
||||
return (c["desc"] != m.desc or
|
||||
due_month_year_to_date(c) != m.due or
|
||||
c["resolved"] != m.resolved or
|
||||
set(c["docs"]) != set(m.docs.all()) or
|
||||
c.get("review") in ("accept", "reject")
|
||||
)
|
||||
|
||||
def save_milestone_form(f):
|
||||
c = f.cleaned_data
|
||||
|
||||
|
@ -183,14 +170,14 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
|||
|
||||
changes = ['Changed %s' % named_milestone]
|
||||
|
||||
if m.state_id == "review" and not needs_review and c["accept"] != "noaction":
|
||||
if m.state_id == "review" and not needs_review and c["review"] != "noaction":
|
||||
if not history:
|
||||
history = save_milestone_in_history(m)
|
||||
|
||||
if c["accept"] == "accept":
|
||||
if c["review"] == "accept":
|
||||
m.state_id = "active"
|
||||
changes.append("set state to active from review, accepting new milestone")
|
||||
elif c["accept"] == "reject":
|
||||
elif c["review"] == "reject":
|
||||
m.state_id = "deleted"
|
||||
changes.append("set state to deleted from review, rejecting new milestone")
|
||||
|
||||
|
@ -260,8 +247,6 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
|||
elif m.state_id == "review":
|
||||
return 'Added %s for review, due %s' % (named_milestone, m.due.strftime("%B %Y"))
|
||||
|
||||
finished_milestone_text = "Done"
|
||||
|
||||
form_errors = False
|
||||
|
||||
if request.method == 'POST':
|
||||
|
@ -272,22 +257,23 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
|||
|
||||
# new milestones have non-existing ids so instance end up as None
|
||||
instance = milestones_dict.get(request.POST.get(prefix + "-id", ""), None)
|
||||
f = MilestoneForm(request.POST, prefix=prefix, instance=instance,
|
||||
needs_review=needs_review)
|
||||
f = MilestoneForm(needs_review, reviewer, request.POST, prefix=prefix, instance=instance)
|
||||
forms.append(f)
|
||||
|
||||
form_errors = form_errors or not f.is_valid()
|
||||
|
||||
f.changed = milestone_changed(f, f.milestone)
|
||||
if f.is_valid() and f.cleaned_data.get("review") in ("accept", "reject"):
|
||||
f.needs_review = False
|
||||
|
||||
action = request.POST.get("action", "review")
|
||||
if action == "review":
|
||||
for f in forms:
|
||||
if not f.is_valid():
|
||||
continue
|
||||
|
||||
# let's fill in the form milestone so we can output it in the template
|
||||
if not f.milestone:
|
||||
f.milestone = GroupMilestone()
|
||||
set_attributes_from_form(f, f.milestone)
|
||||
if f.is_valid():
|
||||
# let's fill in the form milestone so we can output it in the template
|
||||
if not f.milestone:
|
||||
f.milestone = GroupMilestone()
|
||||
set_attributes_from_form(f, f.milestone)
|
||||
elif action == "save" and not form_errors:
|
||||
changes = []
|
||||
for f in forms:
|
||||
|
@ -314,11 +300,11 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
|||
return HttpResponseRedirect(group.about_url())
|
||||
else:
|
||||
for m in milestones:
|
||||
forms.append(MilestoneForm(instance=m, needs_review=needs_review))
|
||||
forms.append(MilestoneForm(needs_review, reviewer, instance=m))
|
||||
|
||||
can_reset = milestone_set == "charter" and get_chartering_type(group.charter) == "rechartering"
|
||||
|
||||
empty_form = MilestoneForm(needs_review=needs_review)
|
||||
empty_form = MilestoneForm(needs_review, reviewer)
|
||||
|
||||
forms.sort(key=lambda f: f.milestone.due if f.milestone else datetime.date.max)
|
||||
|
||||
|
@ -329,9 +315,8 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
|||
form_errors=form_errors,
|
||||
empty_form=empty_form,
|
||||
milestone_set=milestone_set,
|
||||
finished_milestone_text=finished_milestone_text,
|
||||
needs_review=needs_review,
|
||||
reviewer=milestone_reviewer_for_group_type(group_type),
|
||||
reviewer=reviewer,
|
||||
can_reset=can_reset))
|
||||
|
||||
@login_required
|
||||
|
@ -391,8 +376,3 @@ def reset_charter_milestones(request, group_type, acronym):
|
|||
charter_milestones=charter_milestones,
|
||||
current_milestones=current_milestones,
|
||||
))
|
||||
|
||||
|
||||
def ajax_search_docs(request, group_type, acronym):
|
||||
docs = Document.objects.filter(name__icontains=request.GET.get('q',''), type="draft").order_by('name').distinct()[:20]
|
||||
return HttpResponse(json_doc_names(docs), content_type='application/json')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import os
|
||||
import shutil
|
||||
import calendar
|
||||
import json
|
||||
import datetime
|
||||
|
||||
from pyquery import PyQuery
|
||||
|
@ -99,7 +98,7 @@ class GroupPagesTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1)
|
||||
self.assertEqual(len(q('.content-wrapper a:contains("%s")' % group.acronym)), 1)
|
||||
|
||||
def test_concluded_groups(self):
|
||||
draft = make_test_data()
|
||||
|
@ -111,7 +110,7 @@ class GroupPagesTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('table.concluded-groups a:contains("%s")' % group.acronym)), 1)
|
||||
self.assertEqual(len(q('.content-wrapper a:contains("%s")' % group.acronym)), 1)
|
||||
|
||||
def test_bofs(self):
|
||||
draft = make_test_data()
|
||||
|
@ -123,7 +122,7 @@ class GroupPagesTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1)
|
||||
self.assertEqual(len(q('.content-wrapper a:contains("%s")' % group.acronym)), 1)
|
||||
|
||||
def test_group_documents(self):
|
||||
draft = make_test_data()
|
||||
|
@ -302,7 +301,7 @@ class GroupEditTests(TestCase):
|
|||
r = self.client.post(url, dict(acronym="foobarbaz")) # No name
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
self.assertEqual(len(Group.objects.filter(type="wg")), num_wgs)
|
||||
|
||||
# acronym contains non-alphanumeric
|
||||
|
@ -330,7 +329,7 @@ class GroupEditTests(TestCase):
|
|||
self.assertEqual(group.charter.name, "charter-ietf-testwg")
|
||||
self.assertEqual(group.charter.rev, "00-00")
|
||||
|
||||
def test_create_based_on_existing(self):
|
||||
def test_create_based_on_existing_bof(self):
|
||||
make_test_data()
|
||||
|
||||
url = urlreverse('group_create', kwargs=dict(group_type="wg"))
|
||||
|
@ -342,8 +341,8 @@ class GroupEditTests(TestCase):
|
|||
r = self.client.post(url, dict(name="Test", acronym=group.parent.acronym))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertEqual(len(q('form input[name="confirmed"]')), 0) # can't confirm us out of this
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
self.assertEqual(len(q('form input[name="confirm_acronym"]')), 0) # can't confirm us out of this
|
||||
|
||||
# try elevating BoF to WG
|
||||
group.state_id = "bof"
|
||||
|
@ -352,14 +351,14 @@ class GroupEditTests(TestCase):
|
|||
r = self.client.post(url, dict(name="Test", acronym=group.acronym))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertEqual(len(q('form input[name="confirmed"]')), 1)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
self.assertEqual(len(q('form input[name="confirm_acronym"]')), 1)
|
||||
|
||||
self.assertEqual(Group.objects.get(acronym=group.acronym).state_id, "bof")
|
||||
|
||||
# confirm elevation
|
||||
state = GroupStateName.objects.get(slug="proposed")
|
||||
r = self.client.post(url, dict(name="Test", acronym=group.acronym, confirmed="1",state=state.pk))
|
||||
r = self.client.post(url, dict(name="Test", acronym=group.acronym, confirm_acronym="1", state=state.pk))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(Group.objects.get(acronym=group.acronym).state_id, "proposed")
|
||||
self.assertEqual(Group.objects.get(acronym=group.acronym).name, "Test")
|
||||
|
@ -383,7 +382,7 @@ class GroupEditTests(TestCase):
|
|||
r = self.client.post(url, dict(acronym="collide"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# create old acronym
|
||||
group.acronym = "oldmars"
|
||||
|
@ -396,7 +395,7 @@ class GroupEditTests(TestCase):
|
|||
r = self.client.post(url, dict(acronym="oldmars"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# edit info
|
||||
with open(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev)), "w") as f:
|
||||
|
@ -453,7 +452,7 @@ class GroupEditTests(TestCase):
|
|||
r = self.client.post(url, dict(instructions="")) # No instructions
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
|
||||
# request conclusion
|
||||
mailbox_before = len(outbox)
|
||||
|
@ -530,15 +529,14 @@ class MilestoneTests(TestCase):
|
|||
r = self.client.post(url, { 'prefix': "m-1",
|
||||
'm-1-id': "-1",
|
||||
'm-1-desc': "", # no description
|
||||
'm-1-due_month': str(due.month),
|
||||
'm-1-due_year': str(due.year),
|
||||
'm-1-due': due.strftime("%B %Y"),
|
||||
'm-1-resolved': "",
|
||||
'm-1-docs': ",".join(docs),
|
||||
'action': "save",
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
self.assertEqual(GroupMilestone.objects.count(), milestones_before)
|
||||
|
||||
# add
|
||||
|
@ -546,8 +544,7 @@ class MilestoneTests(TestCase):
|
|||
r = self.client.post(url, { 'prefix': "m-1",
|
||||
'm-1-id': "-1",
|
||||
'm-1-desc': "Test 3",
|
||||
'm-1-due_month': str(due.month),
|
||||
'm-1-due_year': str(due.year),
|
||||
'm-1-due': due.strftime("%B %Y"),
|
||||
'm-1-resolved': "",
|
||||
'm-1-docs': ",".join(docs),
|
||||
'action': "save",
|
||||
|
@ -584,8 +581,7 @@ class MilestoneTests(TestCase):
|
|||
r = self.client.post(url, { 'prefix': "m-1",
|
||||
'm-1-id': -1,
|
||||
'm-1-desc': "Test 3",
|
||||
'm-1-due_month': str(due.month),
|
||||
'm-1-due_year': str(due.year),
|
||||
'm-1-due': due.strftime("%B %Y"),
|
||||
'm-1-resolved': "",
|
||||
'm-1-docs': "",
|
||||
'action': "save",
|
||||
|
@ -619,11 +615,10 @@ class MilestoneTests(TestCase):
|
|||
r = self.client.post(url, { 'prefix': "m1",
|
||||
'm1-id': m1.id,
|
||||
'm1-desc': m1.desc,
|
||||
'm1-due_month': str(m1.due.month),
|
||||
'm1-due_year': str(m1.due.year),
|
||||
'm1-due': m1.due.strftime("%B %Y"),
|
||||
'm1-resolved': m1.resolved,
|
||||
'm1-docs': ",".join(m1.docs.values_list("name", flat=True)),
|
||||
'm1-accept': "accept",
|
||||
'm1-review': "accept",
|
||||
'action': "save",
|
||||
})
|
||||
self.assertEqual(r.status_code, 302)
|
||||
|
@ -646,8 +641,7 @@ class MilestoneTests(TestCase):
|
|||
r = self.client.post(url, { 'prefix': "m1",
|
||||
'm1-id': m1.id,
|
||||
'm1-desc': m1.desc,
|
||||
'm1-due_month': str(m1.due.month),
|
||||
'm1-due_year': str(m1.due.year),
|
||||
'm1-due': m1.due.strftime("%B %Y"),
|
||||
'm1-resolved': "",
|
||||
'm1-docs': ",".join(m1.docs.values_list("name", flat=True)),
|
||||
'm1-delete': "checked",
|
||||
|
@ -677,15 +671,14 @@ class MilestoneTests(TestCase):
|
|||
r = self.client.post(url, { 'prefix': "m1",
|
||||
'm1-id': m1.id,
|
||||
'm1-desc': "", # no description
|
||||
'm1-due_month': str(due.month),
|
||||
'm1-due_year': str(due.year),
|
||||
'm1-due': due.strftime("%B %Y"),
|
||||
'm1-resolved': "",
|
||||
'm1-docs': ",".join(docs),
|
||||
'action': "save",
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertTrue(len(q('form .has-error')) > 0)
|
||||
m = GroupMilestone.objects.get(pk=m1.pk)
|
||||
self.assertEqual(GroupMilestone.objects.count(), milestones_before)
|
||||
self.assertEqual(m.due, m1.due)
|
||||
|
@ -695,8 +688,7 @@ class MilestoneTests(TestCase):
|
|||
r = self.client.post(url, { 'prefix': "m1",
|
||||
'm1-id': m1.id,
|
||||
'm1-desc': "Test 2 - changed",
|
||||
'm1-due_month': str(due.month),
|
||||
'm1-due_year': str(due.year),
|
||||
'm1-due': due.strftime("%B %Y"),
|
||||
'm1-resolved': "Done",
|
||||
'm1-resolved_checkbox': "checked",
|
||||
'm1-docs': ",".join(docs),
|
||||
|
@ -873,15 +865,6 @@ class MilestoneTests(TestCase):
|
|||
self.assertTrue(m1.desc in unicode(outbox[-1]))
|
||||
self.assertTrue(m2.desc in unicode(outbox[-1]))
|
||||
|
||||
def test_ajax_search_docs(self):
|
||||
draft = make_test_data()
|
||||
|
||||
r = self.client.get(urlreverse("group_ajax_search_docs", kwargs=dict(group_type=draft.group.type_id, acronym=draft.group.acronym)),
|
||||
dict(q=draft.name))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
data = json.loads(r.content)
|
||||
self.assertTrue(data[0]["id"], draft.name)
|
||||
|
||||
class CustomizeWorkflowTests(TestCase):
|
||||
def test_customize_workflow(self):
|
||||
make_test_data()
|
||||
|
|
|
@ -22,7 +22,6 @@ urlpatterns = patterns('',
|
|||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "current"}, "group_edit_milestones"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "charter"}, "group_edit_charter_milestones"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/reset/$', 'ietf.group.milestones.reset_charter_milestones', None, "group_reset_charter_milestones"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/ajax/searchdocs/$', 'ietf.group.milestones.ajax_search_docs', None, "group_ajax_search_docs"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/workflow/$', 'ietf.group.edit.customize_workflow'),
|
||||
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/about/(?P<group_type>.)?$', 'ietf.group.info.group_about', None, 'group_about'),
|
||||
|
|
|
@ -31,6 +31,5 @@ urlpatterns = patterns('',
|
|||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/$', milestones.edit_milestones, {'milestone_set': "current"}, "group_edit_milestones"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/$', milestones.edit_milestones, {'milestone_set': "charter"}, "group_edit_charter_milestones"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/milestones/charter/reset/$', milestones.reset_charter_milestones, None, "group_reset_charter_milestones"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/ajax/searchdocs/$', milestones.ajax_search_docs, None, "group_ajax_search_docs"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/workflow/$', edit.customize_workflow),
|
||||
)
|
||||
|
|
|
@ -10,7 +10,7 @@ from ietf.group.models import Group, GroupEvent, Role
|
|||
from ietf.group.utils import save_group_in_history
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.name.models import StreamName
|
||||
from ietf.person.fields import AutocompletedEmailsField
|
||||
from ietf.person.fields import SearchableEmailsField
|
||||
from ietf.person.models import Email
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -24,14 +24,16 @@ def stream_documents(request, acronym):
|
|||
streams = [ s.slug for s in StreamName.objects.all().exclude(slug__in=['ietf', 'legacy']) ]
|
||||
if not acronym in streams:
|
||||
raise Http404("No such stream: %s" % acronym)
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
editable = has_role(request.user, "Secretariat") or group.has_role(request.user, "chair")
|
||||
stream = StreamName.objects.get(slug=acronym)
|
||||
form = SearchForm({'by':'stream', 'stream':acronym,
|
||||
'rfcs':'on', 'activedrafts':'on'})
|
||||
docs, meta = retrieve_search_results(form)
|
||||
return render_to_response('group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta }, context_instance=RequestContext(request))
|
||||
return render_to_response('group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta, 'editable':editable }, context_instance=RequestContext(request))
|
||||
|
||||
class StreamEditForm(forms.Form):
|
||||
delegates = AutocompletedEmailsField(required=False, only_users=True)
|
||||
delegates = SearchableEmailsField(required=False, only_users=True)
|
||||
|
||||
def stream_edit(request, acronym):
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
|
@ -62,7 +64,7 @@ def stream_edit(request, acronym):
|
|||
for e in new:
|
||||
Role.objects.get_or_create(name_id=slug, email=e, group=group, person=e.person)
|
||||
|
||||
return redirect("ietf.group.views.streams")
|
||||
return redirect("ietf.group.views_stream.streams")
|
||||
else:
|
||||
form = StreamEditForm(initial=dict(delegates=Email.objects.filter(role__group=group, role__name="delegate")))
|
||||
|
||||
|
@ -72,4 +74,4 @@ def stream_edit(request, acronym):
|
|||
'form': form,
|
||||
},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
|
|
|
@ -85,50 +85,50 @@ def get_doc_section(doc):
|
|||
def agenda_sections():
|
||||
return OrderedDict([
|
||||
('1', {'title':"Administrivia"}),
|
||||
('1.1', {'title':"Roll Call"}),
|
||||
('1.2', {'title':"Bash the Agenda"}),
|
||||
('1.3', {'title':"Approval of the Minutes of Past Telechats"}),
|
||||
('1.4', {'title':"List of Remaining Action Items from Last Telechat"}),
|
||||
('2', {'title':"Protocol Actions"}),
|
||||
('2.1', {'title':"WG Submissions"}),
|
||||
('2.1.1', {'title':"New Items", 'docs': []}),
|
||||
('2.1.2', {'title':"Returning Items", 'docs':[]}),
|
||||
('2.1.3', {'title':"For Action", 'docs':[]}),
|
||||
('2.2', {'title':"Individual Submissions"}),
|
||||
('2.2.1', {'title':"New Items", 'docs':[]}),
|
||||
('2.2.2', {'title':"Returning Items", 'docs':[]}),
|
||||
('2.2.3', {'title':"For Action", 'docs':[]}),
|
||||
('2.3', {'title':"Status Changes"}),
|
||||
('2.3.1', {'title':"New Items", 'docs':[]}),
|
||||
('2.3.2', {'title':"Returning Items", 'docs':[]}),
|
||||
('2.3.3', {'title':"For Action", 'docs':[]}),
|
||||
('3', {'title':"Document Actions"}),
|
||||
('3.1', {'title':"WG Submissions"}),
|
||||
('3.1.1', {'title':"New Items", 'docs':[]}),
|
||||
('3.1.2', {'title':"Returning Items", 'docs':[]}),
|
||||
('3.1.3', {'title':"For Action", 'docs':[]}),
|
||||
('3.2', {'title':"Individual Submissions Via AD"}),
|
||||
('3.2.1', {'title':"New Items", 'docs':[]}),
|
||||
('3.2.2', {'title':"Returning Items", 'docs':[]}),
|
||||
('3.2.3', {'title':"For Action", 'docs':[]}),
|
||||
('3.3', {'title':"Status Changes"}),
|
||||
('3.3.1', {'title':"New Items", 'docs':[]}),
|
||||
('3.3.2', {'title':"Returning Items", 'docs':[]}),
|
||||
('3.3.3', {'title':"For Action", 'docs':[]}),
|
||||
('3.4', {'title':"IRTF and Independent Submission Stream Documents"}),
|
||||
('3.4.1', {'title':"New Items", 'docs':[]}),
|
||||
('3.4.2', {'title':"Returning Items", 'docs':[]}),
|
||||
('3.4.3', {'title':"For Action", 'docs':[]}),
|
||||
('4', {'title':"Working Group Actions"}),
|
||||
('4.1', {'title':"WG Creation"}),
|
||||
('4.1.1', {'title':"Proposed for IETF Review", 'docs':[]}),
|
||||
('4.1.2', {'title':"Proposed for Approval", 'docs':[]}),
|
||||
('4.2', {'title':"WG Rechartering"}),
|
||||
('4.2.1', {'title':"Under Evaluation for IETF Review", 'docs':[]}),
|
||||
('4.2.2', {'title':"Proposed for Approval", 'docs':[]}),
|
||||
('5', {'title':"IAB News We Can Use"}),
|
||||
('6', {'title':"Management Issues"}),
|
||||
('7', {'title':"Working Group News"}),
|
||||
('1.1', {'title':"Roll call"}),
|
||||
('1.2', {'title':"Bash the agenda"}),
|
||||
('1.3', {'title':"Approval of the minutes of past telechats"}),
|
||||
('1.4', {'title':"List of remaining action items from last telechat"}),
|
||||
('2', {'title':"Protocol actions"}),
|
||||
('2.1', {'title':"WG submissions"}),
|
||||
('2.1.1', {'title':"New items", 'docs': []}),
|
||||
('2.1.2', {'title':"Returning items", 'docs':[]}),
|
||||
('2.1.3', {'title':"For action", 'docs':[]}),
|
||||
('2.2', {'title':"Individual submissions"}),
|
||||
('2.2.1', {'title':"New items", 'docs':[]}),
|
||||
('2.2.2', {'title':"Returning items", 'docs':[]}),
|
||||
('2.2.3', {'title':"For action", 'docs':[]}),
|
||||
('2.3', {'title':"Status changes"}),
|
||||
('2.3.1', {'title':"New items", 'docs':[]}),
|
||||
('2.3.2', {'title':"Returning items", 'docs':[]}),
|
||||
('2.3.3', {'title':"For action", 'docs':[]}),
|
||||
('3', {'title':"Document actions"}),
|
||||
('3.1', {'title':"WG submissions"}),
|
||||
('3.1.1', {'title':"New items", 'docs':[]}),
|
||||
('3.1.2', {'title':"Returning items", 'docs':[]}),
|
||||
('3.1.3', {'title':"For action", 'docs':[]}),
|
||||
('3.2', {'title':"Individual submissions via AD"}),
|
||||
('3.2.1', {'title':"New items", 'docs':[]}),
|
||||
('3.2.2', {'title':"Returning items", 'docs':[]}),
|
||||
('3.2.3', {'title':"For action", 'docs':[]}),
|
||||
('3.3', {'title':"Status changes"}),
|
||||
('3.3.1', {'title':"New items", 'docs':[]}),
|
||||
('3.3.2', {'title':"Returning items", 'docs':[]}),
|
||||
('3.3.3', {'title':"For action", 'docs':[]}),
|
||||
('3.4', {'title':"IRTF and Independent Submission stream documents"}),
|
||||
('3.4.1', {'title':"New items", 'docs':[]}),
|
||||
('3.4.2', {'title':"Returning items", 'docs':[]}),
|
||||
('3.4.3', {'title':"For action", 'docs':[]}),
|
||||
('4', {'title':"Working Group actions"}),
|
||||
('4.1', {'title':"WG creation"}),
|
||||
('4.1.1', {'title':"Proposed for IETF review", 'docs':[]}),
|
||||
('4.1.2', {'title':"Proposed for approval", 'docs':[]}),
|
||||
('4.2', {'title':"WG rechartering"}),
|
||||
('4.2.1', {'title':"Under evaluation for IETF review", 'docs':[]}),
|
||||
('4.2.2', {'title':"Proposed for approval", 'docs':[]}),
|
||||
('5', {'title':"IAB news we can use"}),
|
||||
('6', {'title':"Management issues"}),
|
||||
('7', {'title':"Working Group news"}),
|
||||
])
|
||||
|
||||
def fill_in_agenda_administrivia(date, sections):
|
||||
|
@ -185,7 +185,7 @@ def fill_in_agenda_docs(date, sections, matches=None):
|
|||
|
||||
# prune empty "For action" sections
|
||||
empty_for_action = [n for n, section in sections.iteritems()
|
||||
if section["title"] == "For Action" and not section["docs"]]
|
||||
if section["title"] == "For action" and not section["docs"]]
|
||||
for num in empty_for_action:
|
||||
del sections[num]
|
||||
|
||||
|
|
|
@ -2,24 +2,24 @@
|
|||
|
||||
# Portion Copyright (C) 2008-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
|
||||
# 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
|
||||
|
@ -58,6 +58,9 @@ from ietf.iesg.models import TelechatDate
|
|||
from ietf.ietfauth.utils import has_role, role_required, user_is_person
|
||||
from ietf.person.models import Person
|
||||
|
||||
# FACELIFT:
|
||||
from ietf.doc.views_search import fill_in_search_attributes
|
||||
|
||||
def review_decisions(request, year=None):
|
||||
events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved"))
|
||||
|
||||
|
@ -176,8 +179,8 @@ def agenda(request, date=None):
|
|||
data = agenda_data(date)
|
||||
|
||||
if has_role(request.user, ["Area Director", "IAB Chair", "Secretariat"]):
|
||||
data["sections"]["1.1"]["title"] = data["sections"]["1.1"]["title"].replace("Roll Call", '<a href="https://www.ietf.org/iesg/internal/rollcall.txt">Roll Call</a>')
|
||||
data["sections"]["1.3"]["title"] = data["sections"]["1.3"]["title"].replace("Minutes", '<a href="https://www.ietf.org/iesg/internal/minutes.txt">Minutes</a>')
|
||||
data["sections"]["1.1"]["title"] = data["sections"]["1.1"]["title"].replace("Roll call", '<a href="https://www.ietf.org/iesg/internal/rollcall.txt">Roll Call</a>')
|
||||
data["sections"]["1.3"]["title"] = data["sections"]["1.3"]["title"].replace("minutes", '<a href="https://www.ietf.org/iesg/internal/minutes.txt">Minutes</a>')
|
||||
|
||||
request.session['ballot_edit_return_point'] = request.path_info
|
||||
return render_to_response("iesg/agenda.html", {
|
||||
|
@ -303,7 +306,7 @@ class RescheduleForm(forms.Form):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
dates = kwargs.pop('telechat_dates')
|
||||
|
||||
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
||||
# telechat choices
|
||||
|
@ -360,6 +363,9 @@ def agenda_documents(request):
|
|||
telechats = []
|
||||
for date in dates:
|
||||
sections = agenda_sections()
|
||||
# FACELIFT: augment the docs with the search attributes, since we're using
|
||||
# the search_result_row view to display them (which expects them)
|
||||
fill_in_search_attributes(docs_by_date[date])
|
||||
fill_in_agenda_docs(date, sections, docs_by_date[date])
|
||||
|
||||
telechats.append({
|
||||
|
|
|
@ -217,7 +217,7 @@ class PersonForm(ModelForm):
|
|||
|
||||
# Make sure the alias table contains any new and/or old names.
|
||||
old_names = set([x.name for x in Alias.objects.filter(person=self.instance)])
|
||||
curr_names = set([x for x in [self.instance.name,
|
||||
curr_names = set([x for x in [self.instance.name,
|
||||
self.instance.ascii,
|
||||
self.instance.ascii_short,
|
||||
self.data['name'],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import datetime
|
||||
import email
|
||||
|
||||
from django.utils.safestring import mark_safe
|
||||
from django import forms
|
||||
|
||||
from ietf.group.models import Group
|
||||
|
@ -102,20 +103,20 @@ class GenericDisclosureForm(forms.Form):
|
|||
"""Custom ModelForm-like form to use for new Generic or NonDocSpecific Iprs.
|
||||
If patent_info is submitted create a NonDocSpecificIprDisclosure object
|
||||
otherwise create a GenericIprDisclosure object."""
|
||||
compliant = forms.BooleanField(required=False)
|
||||
compliant = forms.CharField(label="This disclosure complies with RFC 3979", required=False)
|
||||
holder_legal_name = forms.CharField(max_length=255)
|
||||
notes = forms.CharField(max_length=255,widget=forms.Textarea,required=False)
|
||||
other_designations = forms.CharField(max_length=255,required=False)
|
||||
holder_contact_name = forms.CharField(max_length=255)
|
||||
holder_contact_email = forms.EmailField()
|
||||
holder_contact_info = forms.CharField(max_length=255,widget=forms.Textarea,required=False)
|
||||
holder_contact_name = forms.CharField(label="Name", max_length=255)
|
||||
holder_contact_email = forms.EmailField(label="Email")
|
||||
holder_contact_info = forms.CharField(label="Other Info (address, phone, etc.)", max_length=255,widget=forms.Textarea,required=False)
|
||||
submitter_name = forms.CharField(max_length=255,required=False)
|
||||
submitter_email = forms.EmailField(required=False)
|
||||
patent_info = forms.CharField(max_length=255,widget=forms.Textarea,required=False)
|
||||
patent_info = forms.CharField(max_length=255,widget=forms.Textarea, required=False, help_text="Patent, Serial, Publication, Registration, or Application/File number(s), Date(s) granted or applied for, Country, and any additional notes")
|
||||
has_patent_pending = forms.BooleanField(required=False)
|
||||
statement = forms.CharField(max_length=255,widget=forms.Textarea,required=False)
|
||||
updates = AutocompletedIprDisclosuresField(required=False)
|
||||
same_as_ii_above = forms.BooleanField(required=False)
|
||||
same_as_ii_above = forms.BooleanField(label="Same as in section II above", required=False)
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
super(GenericDisclosureForm, self).__init__(*args,**kwargs)
|
||||
|
@ -155,7 +156,7 @@ class GenericDisclosureForm(forms.Form):
|
|||
|
||||
class IprDisclosureFormBase(forms.ModelForm):
|
||||
"""Base form for Holder and ThirdParty disclosures"""
|
||||
updates = AutocompletedIprDisclosuresField(required=False)
|
||||
updates = AutocompletedIprDisclosuresField(required=False, help_text=mark_safe("If this disclosure <strong>updates</strong> other disclosures identify here which ones. Leave this field blank if this disclosure does not update any prior disclosures. Note: Updates to IPR disclosures must only be made by authorized representatives of the original submitters. Updates will automatically be forwarded to the current Patent Holder's Contact and to the Submitter of the original IPR disclosure."))
|
||||
same_as_ii_above = forms.BooleanField(required=False)
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
|
@ -163,6 +164,22 @@ class IprDisclosureFormBase(forms.ModelForm):
|
|||
self.fields['submitter_name'].required = False
|
||||
self.fields['submitter_email'].required = False
|
||||
self.fields['compliant'].initial = True
|
||||
self.fields['compliant'].label = "This disclosure complies with RFC 3979"
|
||||
if "ietfer_name" in self.fields:
|
||||
self.fields["ietfer_name"].label = "Name"
|
||||
if "ietfer_contact_email" in self.fields:
|
||||
self.fields["ietfer_contact_email"].label = "Email"
|
||||
if "ietfer_contact_info" in self.fields:
|
||||
self.fields["ietfer_contact_info"].label = "Other info"
|
||||
self.fields["ietfer_contact_info"].help_text = "Address, phone, etc."
|
||||
if "patent_info" in self.fields:
|
||||
self.fields["patent_info"].help_text = "Patent, Serial, Publication, Registration, or Application/File number(s), Date(s) granted or applied for, Country, and any additional notes"
|
||||
if "licensing" in self.fields:
|
||||
self.fields["licensing_comments"].label = "Licensing information, comments, notes, or URL for further information"
|
||||
if "submitter_claims_all_terms_disclosed" in self.fields:
|
||||
self.fields["submitter_claims_all_terms_disclosed"].label = "The individual submitting this template represents and warrants that all terms and conditions that must be satisfied for implementers of any covered IETF specification to obtain a license have been disclosed in this IPR disclosure statement"
|
||||
if "same_as_ii_above" in self.fields:
|
||||
self.fields["same_as_ii_above"].label = "Same as in section II above"
|
||||
|
||||
class Meta:
|
||||
"""This will be overridden"""
|
||||
|
@ -267,13 +284,13 @@ class ThirdPartyIprDisclosureForm(IprDisclosureFormBase):
|
|||
|
||||
class SearchForm(forms.Form):
|
||||
state = forms.MultipleChoiceField(choices=STATE_CHOICES,widget=forms.CheckboxSelectMultiple,required=False)
|
||||
draft = forms.CharField(max_length=128,required=False)
|
||||
rfc = forms.IntegerField(required=False)
|
||||
holder = forms.CharField(max_length=128,required=False)
|
||||
patent = forms.CharField(max_length=128,required=False)
|
||||
group = GroupModelChoiceField(label="Working group name",queryset=Group.objects.filter(type='wg').order_by('acronym'),required=False)
|
||||
doctitle = forms.CharField(max_length=128,required=False)
|
||||
iprtitle = forms.CharField(max_length=128,required=False)
|
||||
draft = forms.CharField(label="Draft name", max_length=128, required=False)
|
||||
rfc = forms.IntegerField(label="RFC number", required=False)
|
||||
holder = forms.CharField(label="Name of patent owner/applicant", max_length=128,required=False)
|
||||
patent = forms.CharField(label="Text in patent information", max_length=128,required=False)
|
||||
group = GroupModelChoiceField(label="Working group",queryset=Group.objects.filter(type='wg').order_by('acronym'),required=False, empty_label="(Select WG)")
|
||||
doctitle = forms.CharField(label="Words in document title", max_length=128,required=False)
|
||||
iprtitle = forms.CharField(label="Words in IPR disclosure title", max_length=128,required=False)
|
||||
|
||||
class StateForm(forms.Form):
|
||||
state = forms.ModelChoiceField(queryset=IprDisclosureStateName.objects,label="New State",empty_label=None)
|
||||
|
|
|
@ -12,8 +12,8 @@ LICENSE_CHOICES = (
|
|||
(3, 'c) Reasonable and Non-Discriminatory License to All Implementers with Possible Royalty/Fee.'),
|
||||
(4, 'd) Licensing Declaration to be Provided Later (implies a willingness'
|
||||
' to commit to the provisions of a), b), or c) above to all implementers;'
|
||||
' otherwise, the next option "Unwilling to Commit to the Provisions of'
|
||||
' a), b), or c) Above". - must be selected).'),
|
||||
' otherwise, the next option - "Unwilling to Commit to the Provisions of'
|
||||
' a), b), or c) Above" - must be selected).'),
|
||||
(5, 'e) Unwilling to Commit to the Provisions of a), b), or c) Above.'),
|
||||
(6, 'f) See Text Below for Licensing Declaration.'),
|
||||
)
|
||||
|
@ -27,10 +27,10 @@ SELECT_CHOICES = (
|
|||
(2, 'NO'),
|
||||
)
|
||||
STATUS_CHOICES = (
|
||||
( 0, "Waiting for approval" ),
|
||||
( 1, "Approved and Posted" ),
|
||||
( 2, "Rejected by Administrator" ),
|
||||
( 3, "Removed by Request" ),
|
||||
( 0, "Waiting for approval" ),
|
||||
( 1, "Approved and Posted" ),
|
||||
( 2, "Rejected by Administrator" ),
|
||||
( 3, "Removed by Request" ),
|
||||
)
|
||||
|
||||
class IprDetail(models.Model):
|
||||
|
@ -45,35 +45,35 @@ class IprDetail(models.Model):
|
|||
legacy_title_2 = models.CharField(blank=True, null=True, db_column="additional_old_title2", max_length=255)
|
||||
|
||||
# Patent holder fieldset
|
||||
legal_name = models.CharField("Legal Name", db_column="p_h_legal_name", max_length=255)
|
||||
legal_name = models.CharField("Legal name", db_column="p_h_legal_name", max_length=255)
|
||||
|
||||
# Patent Holder Contact fieldset
|
||||
# self.contact.filter(contact_type=1)
|
||||
|
||||
# IETF Contact fieldset
|
||||
# self.contact.filter(contact_type=3)
|
||||
|
||||
|
||||
# Related IETF Documents fieldset
|
||||
rfc_number = models.IntegerField(null=True, editable=False, blank=True) # always NULL
|
||||
id_document_tag = models.IntegerField(null=True, editable=False, blank=True) # always NULL
|
||||
other_designations = models.CharField(blank=True, max_length=255)
|
||||
other_designations = models.CharField("Designations for other contributions", blank=True, max_length=255)
|
||||
document_sections = models.TextField("Specific document sections covered", blank=True, max_length=255, db_column='disclouser_identify')
|
||||
|
||||
# Patent Information fieldset
|
||||
patents = models.TextField("Patent Applications", db_column="p_applications", max_length=255)
|
||||
date_applied = models.CharField(max_length=255)
|
||||
patents = models.TextField("Patent, serial, publication, registration, or application/file number(s)", db_column="p_applications", max_length=255)
|
||||
date_applied = models.CharField("Date(s) granted or applied for", max_length=255)
|
||||
country = models.CharField(max_length=255)
|
||||
notes = models.TextField("Additional notes", db_column="p_notes", blank=True)
|
||||
is_pending = models.IntegerField("Unpublished Pending Patent Application", blank=True, null=True, choices=SELECT_CHOICES, db_column="selecttype")
|
||||
applies_to_all = models.IntegerField("Applies to all IPR owned by Submitter", blank=True, null=True, choices=SELECT_CHOICES, db_column="selectowned")
|
||||
is_pending = models.IntegerField("Unpublished pending patent application", blank=True, null=True, choices=SELECT_CHOICES, db_column="selecttype")
|
||||
applies_to_all = models.IntegerField("Applies to all IPR owned by submitter", blank=True, null=True, choices=SELECT_CHOICES, db_column="selectowned")
|
||||
|
||||
# Licensing Declaration fieldset
|
||||
licensing_option = models.IntegerField(null=True, blank=True, choices=LICENSE_CHOICES)
|
||||
lic_opt_a_sub = models.IntegerField(null=True, editable=False, choices=STDONLY_CHOICES)
|
||||
lic_opt_b_sub = models.IntegerField(null=True, editable=False, choices=STDONLY_CHOICES)
|
||||
lic_opt_c_sub = models.IntegerField(null=True, editable=False, choices=STDONLY_CHOICES)
|
||||
comments = models.TextField("Licensing Comments", blank=True)
|
||||
lic_checkbox = models.BooleanField("All terms and conditions has been disclosed", default=False)
|
||||
comments = models.TextField("Licensing comments", blank=True)
|
||||
lic_checkbox = models.BooleanField("The individual submitting this template represents and warrants that all terms and conditions that must be satisfied for implementers of any covered IETF specification to obtain a license have been disclosed in this IPR disclosure statement.", default=False)
|
||||
|
||||
|
||||
# Other notes fieldset
|
||||
|
@ -119,8 +119,8 @@ class IprContact(models.Model):
|
|||
name = models.CharField(max_length=255)
|
||||
title = models.CharField(blank=True, max_length=255)
|
||||
department = models.CharField(blank=True, max_length=255)
|
||||
address1 = models.CharField(blank=True, max_length=255)
|
||||
address2 = models.CharField(blank=True, max_length=255)
|
||||
address1 = models.CharField("Address", blank=True, max_length=255)
|
||||
address2 = models.CharField("Address (continued)", blank=True, max_length=255)
|
||||
telephone = models.CharField(blank=True, max_length=25)
|
||||
fax = models.CharField(blank=True, max_length=25)
|
||||
email = models.EmailField(max_length=255)
|
||||
|
@ -269,7 +269,7 @@ class HolderIprDisclosure(IprDisclosureBase):
|
|||
has_patent_pending = models.BooleanField(default=False)
|
||||
holder_contact_email = models.EmailField()
|
||||
holder_contact_name = models.CharField(max_length=255)
|
||||
holder_contact_info = models.TextField(blank=True)
|
||||
holder_contact_info = models.TextField(blank=True, help_text="Address, phone, etc.")
|
||||
licensing = models.ForeignKey(IprLicenseTypeName)
|
||||
licensing_comments = models.TextField(blank=True)
|
||||
submitter_claims_all_terms_disclosed = models.BooleanField(default=False)
|
||||
|
@ -277,7 +277,7 @@ class HolderIprDisclosure(IprDisclosureBase):
|
|||
class ThirdPartyIprDisclosure(IprDisclosureBase):
|
||||
ietfer_name = models.CharField(max_length=255) # "Whose Personal Belief Triggered..."
|
||||
ietfer_contact_email = models.EmailField()
|
||||
ietfer_contact_info = models.TextField(blank=True)
|
||||
ietfer_contact_info = models.TextField(blank=True, help_text="Address, phone, etc.")
|
||||
patent_info = models.TextField()
|
||||
has_patent_pending = models.BooleanField(default=False)
|
||||
|
||||
|
@ -285,7 +285,7 @@ class NonDocSpecificIprDisclosure(IprDisclosureBase):
|
|||
'''A Generic IPR Disclosure w/ patent information'''
|
||||
holder_contact_name = models.CharField(max_length=255)
|
||||
holder_contact_email = models.EmailField()
|
||||
holder_contact_info = models.TextField(blank=True)
|
||||
holder_contact_info = models.TextField(blank=True, help_text="Address, phone, etc.")
|
||||
patent_info = models.TextField()
|
||||
has_patent_pending = models.BooleanField(default=False)
|
||||
statement = models.TextField() # includes licensing info
|
||||
|
@ -293,7 +293,7 @@ class NonDocSpecificIprDisclosure(IprDisclosureBase):
|
|||
class GenericIprDisclosure(IprDisclosureBase):
|
||||
holder_contact_name = models.CharField(max_length=255)
|
||||
holder_contact_email = models.EmailField()
|
||||
holder_contact_info = models.TextField(blank=True)
|
||||
holder_contact_info = models.TextField(blank=True, help_text="Address, phone, etc.")
|
||||
statement = models.TextField() # includes licensing info
|
||||
|
||||
class IprDocRel(models.Model):
|
||||
|
|
|
@ -13,7 +13,7 @@ from ietf.ipr.models import (IprDisclosureBase,GenericIprDisclosure,HolderIprDis
|
|||
ThirdPartyIprDisclosure,RelatedIpr)
|
||||
from ietf.ipr.utils import get_genitive, get_ipr_summary
|
||||
from ietf.message.models import Message
|
||||
from ietf.utils.test_utils import TestCase
|
||||
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
|
||||
from ietf.utils.test_data import make_test_data
|
||||
|
||||
|
||||
|
@ -247,7 +247,7 @@ class IprTests(TestCase):
|
|||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q("ul.errorlist")) > 0)
|
||||
self.assertTrue(len(q("form .has-error")) > 0)
|
||||
|
||||
# successful post
|
||||
r = self.client.post(url, {
|
||||
|
@ -468,14 +468,11 @@ I would like to revoke this declaration.
|
|||
def test_post(self):
|
||||
make_test_data()
|
||||
ipr = IprDisclosureBase.objects.get(title='Statement regarding rights')
|
||||
url = urlreverse("ipr_post",kwargs={ "id": ipr.id })
|
||||
# fail if not logged in
|
||||
url = urlreverse("ipr_post", kwargs={ "id": ipr.id })
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
r = self.client.get(url,follow=True)
|
||||
self.assertTrue("Sign In" in r.content)
|
||||
# successful post
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
r = self.client.get(url,follow=True)
|
||||
self.assertEqual(r.status_code,200)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
ipr = IprDisclosureBase.objects.get(title='Statement regarding rights')
|
||||
self.assertEqual(ipr.state.slug,'posted')
|
||||
|
||||
|
@ -518,4 +515,4 @@ Subject: test
|
|||
""".format(data['reply_to'],datetime.datetime.now().ctime())
|
||||
result = process_response_email(message_string)
|
||||
self.assertIsInstance(result,Message)
|
||||
self.assertFalse(event.response_past_due())
|
||||
self.assertFalse(event.response_past_due())
|
||||
|
|
|
@ -10,8 +10,7 @@ from django.db.models import Q
|
|||
from django.forms.models import inlineformset_factory
|
||||
from django.forms.formsets import formset_factory
|
||||
from django.http import HttpResponse, Http404, HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response as render, get_object_or_404, redirect
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from ietf.doc.models import DocAlias
|
||||
|
@ -212,7 +211,7 @@ def ajax_rfc_search(request):
|
|||
# Views
|
||||
# ----------------------------------------------------------------
|
||||
def about(request):
|
||||
return render("ipr/disclosure.html", {}, context_instance=RequestContext(request))
|
||||
return render(request, "ipr/disclosure.html", {})
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def add_comment(request, id):
|
||||
|
@ -239,8 +238,7 @@ def add_comment(request, id):
|
|||
else:
|
||||
form = AddCommentForm()
|
||||
|
||||
return render('ipr/add_comment.html',dict(ipr=ipr,form=form),
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'ipr/add_comment.html',dict(ipr=ipr,form=form))
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def add_email(request, id):
|
||||
|
@ -276,8 +274,7 @@ def add_email(request, id):
|
|||
else:
|
||||
form = AddEmailForm(ipr=ipr)
|
||||
|
||||
return render('ipr/add_email.html',dict(ipr=ipr,form=form),
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'ipr/add_email.html',dict(ipr=ipr,form=form))
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def admin(request,state):
|
||||
|
@ -293,12 +290,11 @@ def admin(request,state):
|
|||
('Parked','parked',urlreverse('ipr_admin',kwargs={'state':'parked'}),True)]
|
||||
|
||||
template = 'ipr/admin_' + state + '.html'
|
||||
return render(template, {
|
||||
return render(request, template, {
|
||||
'iprs': iprs,
|
||||
'tabs': tabs,
|
||||
'selected': state},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'selected': state
|
||||
})
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def edit(request, id, updates=None):
|
||||
|
@ -384,13 +380,12 @@ def edit(request, id, updates=None):
|
|||
draft_formset = DraftFormset(instance=ipr, prefix='draft',queryset=dqs)
|
||||
rfc_formset = RfcFormset(instance=ipr, prefix='rfc',queryset=rqs)
|
||||
|
||||
return render("ipr/details_edit.html", {
|
||||
return render(request, "ipr/details_edit.html", {
|
||||
'form': form,
|
||||
'draft_formset':draft_formset,
|
||||
'rfc_formset':rfc_formset,
|
||||
'type':type},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'type':type
|
||||
})
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def email(request, id):
|
||||
|
@ -441,11 +436,10 @@ def email(request, id):
|
|||
}
|
||||
form = MessageModelForm(initial=initial)
|
||||
|
||||
return render("ipr/email.html", {
|
||||
return render(request, "ipr/email.html", {
|
||||
'ipr': ipr,
|
||||
'form':form},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'form':form
|
||||
})
|
||||
|
||||
def history(request, id):
|
||||
"""Show the history for a specific IPR disclosure"""
|
||||
|
@ -457,13 +451,12 @@ def history(request, id):
|
|||
tabs = [('Disclosure','disclosure',urlreverse('ipr_show',kwargs={'id':id}),True),
|
||||
('History','history',urlreverse('ipr_history',kwargs={'id':id}),True)]
|
||||
|
||||
return render("ipr/details_history.html", {
|
||||
return render(request, "ipr/details_history.html", {
|
||||
'events':events,
|
||||
'ipr': ipr,
|
||||
'tabs':tabs,
|
||||
'selected':'history'},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'selected':'history'
|
||||
})
|
||||
|
||||
def iprs_for_drafts_txt(request):
|
||||
docipr = {}
|
||||
|
@ -544,7 +537,7 @@ def new(request, type, updates=None):
|
|||
"ipr/new_update_email.txt",
|
||||
{"ipr": disclosure,})
|
||||
|
||||
return render("ipr/submitted.html", context_instance=RequestContext(request))
|
||||
return render(request, "ipr/submitted.html")
|
||||
|
||||
else:
|
||||
if updates:
|
||||
|
@ -555,13 +548,12 @@ def new(request, type, updates=None):
|
|||
draft_formset = DraftFormset(instance=disclosure, prefix='draft')
|
||||
rfc_formset = RfcFormset(instance=disclosure, prefix='rfc')
|
||||
|
||||
return render("ipr/details_edit.html", {
|
||||
return render(request, "ipr/details_edit.html", {
|
||||
'form': form,
|
||||
'draft_formset':draft_formset,
|
||||
'rfc_formset':rfc_formset,
|
||||
'type':type},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'type':type,
|
||||
})
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def notify(request, id, type):
|
||||
|
@ -597,11 +589,10 @@ def notify(request, id, type):
|
|||
initial = [ {'type':'msgout','text':m} for m in get_posted_emails(ipr) ]
|
||||
formset = NotifyFormset(initial=initial)
|
||||
|
||||
return render("ipr/notify.html", {
|
||||
return render(request, "ipr/notify.html", {
|
||||
'formset': formset,
|
||||
'ipr': ipr},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'ipr': ipr,
|
||||
})
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def post(request, id):
|
||||
|
@ -731,44 +722,41 @@ def search(request):
|
|||
iprs = sorted(iprs, key=lambda x: x.state.order)
|
||||
else:
|
||||
iprs = sorted(iprs, key=lambda x: (x.time, x.id), reverse=True)
|
||||
|
||||
return render(template, {
|
||||
|
||||
return render(request, template, {
|
||||
"q": q,
|
||||
"iprs": iprs,
|
||||
"docs": docs,
|
||||
"doc": doc,
|
||||
"form":form,
|
||||
"states":states},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
"states":states
|
||||
})
|
||||
|
||||
return HttpResponseRedirect(request.path)
|
||||
|
||||
else:
|
||||
form = SearchForm(initial={'state':['all']})
|
||||
return render("ipr/search.html", {"form":form }, context_instance=RequestContext(request))
|
||||
return render(request, "ipr/search.html", {"form":form })
|
||||
|
||||
def show(request, id):
|
||||
"""View of individual declaration"""
|
||||
ipr = get_object_or_404(IprDisclosureBase, id=id).get_child()
|
||||
if not has_role(request.user, 'Secretariat'):
|
||||
if ipr.state.slug == 'removed':
|
||||
return render("ipr/removed.html", {
|
||||
'ipr': ipr},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
return render(request, "ipr/removed.html", {
|
||||
'ipr': ipr
|
||||
})
|
||||
elif ipr.state.slug != 'posted':
|
||||
raise Http404
|
||||
|
||||
tabs = [('Disclosure','disclosure',urlreverse('ipr_show',kwargs={'id':id}),True),
|
||||
('History','history',urlreverse('ipr_history',kwargs={'id':id}),True)]
|
||||
|
||||
return render("ipr/details_view.html", {
|
||||
return render(request, "ipr/details_view.html", {
|
||||
'ipr': ipr,
|
||||
'tabs':tabs,
|
||||
'selected':'disclosure'},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'selected':'disclosure',
|
||||
})
|
||||
|
||||
def showlist(request):
|
||||
"""List all disclosures by type, posted only"""
|
||||
|
@ -781,12 +769,11 @@ def showlist(request):
|
|||
generic = itertools.chain(generic,nondocspecific)
|
||||
generic = sorted(generic, key=lambda x: x.time,reverse=True)
|
||||
|
||||
return render("ipr/list.html", {
|
||||
return render(request, "ipr/list.html", {
|
||||
'generic_disclosures' : generic,
|
||||
'specific_disclosures': specific,
|
||||
'thirdpty_disclosures': thirdpty},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
'thirdpty_disclosures': thirdpty,
|
||||
})
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def state(request, id):
|
||||
|
@ -822,8 +809,7 @@ def state(request, id):
|
|||
else:
|
||||
form = StateForm(initial={'state':ipr.state.pk,'private':True})
|
||||
|
||||
return render('ipr/state.html',dict(ipr=ipr,form=form),
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'ipr/state.html', dict(ipr=ipr, form=form))
|
||||
|
||||
# use for link to update specific IPR
|
||||
def update(request, id):
|
||||
|
@ -832,4 +818,4 @@ def update(request, id):
|
|||
ipr = get_object_or_404(IprDisclosureBase,id=id)
|
||||
child = ipr.get_child()
|
||||
type = class_to_type[child.__class__.__name__]
|
||||
return new(request, type, updates=id)
|
||||
return new(request, type, updates=id)
|
||||
|
|
50
ietf/liaisons/fields.py
Normal file
50
ietf/liaisons/fields.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import json
|
||||
|
||||
from django.utils.html import escape
|
||||
from django import forms
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
from ietf.liaisons.models import LiaisonStatement
|
||||
|
||||
def select2_id_liaison_json(objs):
|
||||
return json.dumps([{ "id": o.pk, "text": escape(o.title) } for o in objs])
|
||||
|
||||
class SearchableLiaisonStatementField(forms.IntegerField):
|
||||
"""Server-based multi-select field for choosing liaison statements using
|
||||
select2.js."""
|
||||
|
||||
def __init__(self, hint_text="Type in title to search for document", *args, **kwargs):
|
||||
super(SearchableLiaisonStatementField, self).__init__(*args, **kwargs)
|
||||
|
||||
self.widget.attrs["class"] = "select2-field"
|
||||
self.widget.attrs["data-placeholder"] = hint_text
|
||||
self.widget.attrs["data-max-entries"] = 1
|
||||
|
||||
def prepare_value(self, value):
|
||||
if not value:
|
||||
value = None
|
||||
elif isinstance(value, LiaisonStatement):
|
||||
value = value
|
||||
else:
|
||||
value = LiaisonStatement.objects.exclude(approved=None).filter(pk=value).first()
|
||||
|
||||
self.widget.attrs["data-pre"] = select2_id_liaison_json([value] if value else [])
|
||||
|
||||
# doing this in the constructor is difficult because the URL
|
||||
# patterns may not have been fully constructed there yet
|
||||
self.widget.attrs["data-ajax-url"] = urlreverse("ajax_select2_search_liaison_statements")
|
||||
|
||||
return value
|
||||
|
||||
def clean(self, value):
|
||||
value = super(SearchableLiaisonStatementField, self).clean(value)
|
||||
|
||||
if value == None:
|
||||
return None
|
||||
|
||||
obj = LiaisonStatement.objects.filter(pk=value).first()
|
||||
if not obj and self.required:
|
||||
raise forms.ValidationError(u"You must select a value.")
|
||||
|
||||
return obj
|
||||
|
|
@ -11,11 +11,13 @@ from ietf.liaisons.accounts import (can_add_outgoing_liaison, can_add_incoming_l
|
|||
get_person_for_user, is_secretariat, is_sdo_liaison_manager)
|
||||
from ietf.liaisons.utils import IETFHM
|
||||
from ietf.liaisons.widgets import (FromWidget, ReadOnlyWidget, ButtonWidget,
|
||||
ShowAttachmentsWidget, RelatedLiaisonWidget)
|
||||
ShowAttachmentsWidget)
|
||||
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName
|
||||
from ietf.liaisons.fields import SearchableLiaisonStatementField
|
||||
from ietf.group.models import Group, Role
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.doc.models import Document
|
||||
from ietf.utils.fields import DatepickerDateField
|
||||
|
||||
|
||||
class LiaisonForm(forms.Form):
|
||||
|
@ -28,8 +30,9 @@ class LiaisonForm(forms.Form):
|
|||
technical_contact = forms.CharField(required=False, max_length=255)
|
||||
cc1 = forms.CharField(widget=forms.Textarea, label="CC", required=False, help_text='Please insert one email address per line')
|
||||
purpose = forms.ChoiceField()
|
||||
deadline_date = forms.DateField(label='Deadline')
|
||||
submitted_date = forms.DateField(label='Submission date', initial=datetime.date.today())
|
||||
related_to = SearchableLiaisonStatementField(label=u'Related Liaison Statement', required=False)
|
||||
deadline_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Deadline', required=True)
|
||||
submitted_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Submission date', required=True, initial=datetime.date.today())
|
||||
title = forms.CharField(label=u'Title')
|
||||
body = forms.CharField(widget=forms.Textarea, required=False)
|
||||
attachments = forms.CharField(label='Attachments', widget=ShowAttachmentsWidget, required=False)
|
||||
|
@ -40,13 +43,12 @@ class LiaisonForm(forms.Form):
|
|||
require=['id_attach_title', 'id_attach_file'],
|
||||
required_label='title and file'),
|
||||
required=False)
|
||||
related_to = forms.ModelChoiceField(LiaisonStatement.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False)
|
||||
|
||||
fieldsets = [('From', ('from_field', 'replyto')),
|
||||
('To', ('organization', 'to_poc')),
|
||||
('Other email addresses', ('response_contact', 'technical_contact', 'cc1')),
|
||||
('Purpose', ('purpose', 'deadline_date')),
|
||||
('References', ('related_to', )),
|
||||
('Reference', ('related_to', )),
|
||||
('Liaison Statement', ('title', 'submitted_date', 'body', 'attachments')),
|
||||
('Add attachment', ('attach_title', 'attach_file', 'attach_button')),
|
||||
]
|
||||
|
@ -80,7 +82,7 @@ class LiaisonForm(forms.Form):
|
|||
self.initial["title"] = self.instance.title
|
||||
self.initial["body"] = self.instance.body
|
||||
self.initial["attachments"] = self.instance.attachments.all()
|
||||
self.initial["related_to"] = self.instance.related_to_id
|
||||
self.initial["related_to"] = self.instance.related_to
|
||||
if "approved" in self.fields:
|
||||
self.initial["approved"] = bool(self.instance.approved)
|
||||
|
||||
|
|
264
ietf/liaisons/migrations/0001_initial.py
Normal file
264
ietf/liaisons/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,264 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import SchemaMigration
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'LiaisonStatement'
|
||||
# db.create_table(u'liaisons_liaisonstatement', (
|
||||
# (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
# ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('purpose', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.LiaisonStatementPurposeName'])),
|
||||
# ('body', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
# ('deadline', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
|
||||
# ('related_to', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['liaisons.LiaisonStatement'], null=True, blank=True)),
|
||||
# ('from_group', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='liaisonstatement_from_set', null=True, to=orm['group.Group'])),
|
||||
# ('from_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
# ('from_contact', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Email'], null=True, blank=True)),
|
||||
# ('to_group', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='liaisonstatement_to_set', null=True, to=orm['group.Group'])),
|
||||
# ('to_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
# ('to_contact', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('reply_to', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('response_contact', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('technical_contact', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('cc', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
# ('submitted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
# ('modified', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
# ('approved', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
# ('action_taken', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
# ))
|
||||
# db.send_create_signal(u'liaisons', ['LiaisonStatement'])
|
||||
|
||||
# # Adding M2M table for field attachments on 'LiaisonStatement'
|
||||
# m2m_table_name = db.shorten_name(u'liaisons_liaisonstatement_attachments')
|
||||
# db.create_table(m2m_table_name, (
|
||||
# ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
# ('liaisonstatement', models.ForeignKey(orm[u'liaisons.liaisonstatement'], null=False)),
|
||||
# ('document', models.ForeignKey(orm[u'doc.document'], null=False))
|
||||
# ))
|
||||
# db.create_unique(m2m_table_name, ['liaisonstatement_id', 'document_id'])
|
||||
pass
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# # Deleting model 'LiaisonStatement'
|
||||
# db.delete_table(u'liaisons_liaisonstatement')
|
||||
|
||||
# # Removing M2M table for field attachments on 'LiaisonStatement'
|
||||
# db.delete_table(db.shorten_name(u'liaisons_liaisonstatement_attachments'))
|
||||
pass
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
u'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
u'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
u'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': u"orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'liaisons.liaisonstatement': {
|
||||
'Meta': {'object_name': 'LiaisonStatement'},
|
||||
'action_taken': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'approved': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'body': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'cc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'deadline': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'from_contact': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']", 'null': 'True', 'blank': 'True'}),
|
||||
'from_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_from_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'from_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'purpose': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.LiaisonStatementPurposeName']"}),
|
||||
'related_to': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['liaisons.LiaisonStatement']", 'null': 'True', 'blank': 'True'}),
|
||||
'reply_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'response_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'submitted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'technical_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_to_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'to_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||
},
|
||||
u'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['liaisons']
|
229
ietf/liaisons/migrations/0002_add_missing_titles.py
Normal file
229
ietf/liaisons/migrations/0002_add_missing_titles.py
Normal file
|
@ -0,0 +1,229 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import DataMigration
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
for l in orm.LiaisonStatement.objects.filter(title=""):
|
||||
a = l.attachments.all().first()
|
||||
if a:
|
||||
l.title = a.title
|
||||
l.save()
|
||||
|
||||
def backwards(self, orm):
|
||||
pass
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
u'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
u'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
u'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': u"orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'liaisons.liaisonstatement': {
|
||||
'Meta': {'object_name': 'LiaisonStatement'},
|
||||
'action_taken': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'approved': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'body': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'cc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'deadline': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'from_contact': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']", 'null': 'True', 'blank': 'True'}),
|
||||
'from_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_from_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'from_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'purpose': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.LiaisonStatementPurposeName']"}),
|
||||
'related_to': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['liaisons.LiaisonStatement']", 'null': 'True', 'blank': 'True'}),
|
||||
'reply_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'response_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'submitted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'technical_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_to_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'to_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||
},
|
||||
u'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['liaisons']
|
||||
symmetrical = True
|
0
ietf/liaisons/migrations/__init__.py
Normal file
0
ietf/liaisons/migrations/__init__.py
Normal file
|
@ -49,4 +49,4 @@ class LiaisonStatement(models.Model):
|
|||
return slugify("liaison" + " " + self.submitted.strftime("%Y-%m-%d") + " " + frm[:50] + " " + to[:50] + " " + self.title[:115])
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title or u"<no title>"
|
||||
return self.title
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.views.generic import RedirectView, TemplateView
|
|||
|
||||
urlpatterns = patterns('',
|
||||
(r'^help/$', TemplateView.as_view(template_name='liaisons/help.html')),
|
||||
(r'^help/fields/$', TemplateView.as_view(template_name='liaisons/field_help.html')),
|
||||
url(r'^help/fields/$', TemplateView.as_view(template_name='liaisons/field_help.html'), name="liaisons_field_help"),
|
||||
(r'^help/from_ietf/$', TemplateView.as_view(template_name='liaisons/guide_from_ietf.html')),
|
||||
(r'^help/to_ietf/$', TemplateView.as_view(template_name='liaisons/guide_to_ietf.html')),
|
||||
(r'^managers/$', RedirectView.as_view(url='http://www.ietf.org/liaison/managers.html')),
|
||||
|
@ -18,6 +18,6 @@ urlpatterns += patterns('ietf.liaisons.views',
|
|||
url(r'^for_approval/$', 'liaison_approval_list', name='liaison_approval_list'),
|
||||
url(r'^for_approval/(?P<object_id>\d+)/$', 'liaison_approval_detail', name='liaison_approval_detail'),
|
||||
url(r'^add/$', 'add_liaison', name='add_liaison'),
|
||||
url(r'^ajax/get_info/$', 'get_info'),
|
||||
url(r'^ajax/liaison_list/$', 'ajax_liaison_list', name='ajax_liaison_list'),
|
||||
url(r'^ajax/get_info/$', 'ajax_get_liaison_info'),
|
||||
url(r'^ajax/select2search/$', 'ajax_select2_search_liaison_statements', name='ajax_select2_search_liaison_statements'),
|
||||
)
|
||||
|
|
|
@ -16,6 +16,7 @@ from ietf.liaisons.accounts import (get_person_for_user, can_add_outgoing_liaiso
|
|||
from ietf.liaisons.forms import liaison_form_factory
|
||||
from ietf.liaisons.utils import IETFHM, can_submit_liaison_required, approvable_liaison_statements
|
||||
from ietf.liaisons.mails import notify_pending_by_email, send_liaison_by_email
|
||||
from ietf.liaisons.fields import select2_id_liaison_json
|
||||
|
||||
|
||||
|
||||
|
@ -44,7 +45,7 @@ def add_liaison(request, liaison=None):
|
|||
|
||||
|
||||
@can_submit_liaison_required
|
||||
def get_info(request):
|
||||
def ajax_get_liaison_info(request):
|
||||
person = get_person_for_user(request.user)
|
||||
|
||||
to_entity_id = request.GET.get('to_entity_id', None)
|
||||
|
@ -110,14 +111,20 @@ def liaison_list(request):
|
|||
"sort": sort,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def ajax_liaison_list(request):
|
||||
sort, order_by = normalize_sort(request)
|
||||
liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by)
|
||||
def ajax_select2_search_liaison_statements(request):
|
||||
q = [w.strip() for w in request.GET.get('q', '').split() if w.strip()]
|
||||
|
||||
return render_to_response('liaisons/liaison_table.html', {
|
||||
"liaisons": liaisons,
|
||||
"sort": sort,
|
||||
}, context_instance=RequestContext(request))
|
||||
if not q:
|
||||
objs = LiaisonStatement.objects.none()
|
||||
else:
|
||||
qs = LiaisonStatement.objects.exclude(approved=None).all()
|
||||
|
||||
for t in q:
|
||||
qs = qs.filter(title__icontains=t)
|
||||
|
||||
objs = qs.distinct().order_by("-id")[:20]
|
||||
|
||||
return HttpResponse(select2_id_liaison_json(objs), content_type='application/json')
|
||||
|
||||
@can_submit_liaison_required
|
||||
def liaison_approval_list(request):
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.db.models.query import QuerySet
|
||||
from django.forms.widgets import Select, Widget, TextInput
|
||||
from django.forms.widgets import Select, Widget
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.html import conditional_escape
|
||||
|
||||
from ietf.liaisons.models import LiaisonStatement
|
||||
|
||||
|
||||
class FromWidget(Select):
|
||||
|
||||
|
@ -28,7 +25,7 @@ class FromWidget(Select):
|
|||
value = option[0]
|
||||
text = option[1]
|
||||
base = u'<input type="hidden" value="%s" id="id_%s" name="%s" />%s' % (conditional_escape(value), conditional_escape(name), conditional_escape(name), conditional_escape(text))
|
||||
base += u' (<a class="from_mailto" href="">' + conditional_escape(self.submitter) + u'</a>)'
|
||||
base += u' <a class="from_mailto form-control" href="">' + conditional_escape(self.submitter) + u'</a>'
|
||||
if self.full_power_on:
|
||||
base += '<div style="display: none;" class="reducedToOptions">'
|
||||
for from_code in self.full_power_on:
|
||||
|
@ -40,9 +37,8 @@ class FromWidget(Select):
|
|||
|
||||
|
||||
class ReadOnlyWidget(Widget):
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
html = u'<div id="id_%s">%s</div>' % (conditional_escape(name), conditional_escape(value or ''))
|
||||
html = u'<div id="id_%s" class="form-control" style="height: auto; min-height: 34px;">%s</div>' % (conditional_escape(name), conditional_escape(value or ''))
|
||||
return mark_safe(html)
|
||||
|
||||
|
||||
|
@ -63,7 +59,7 @@ class ButtonWidget(Widget):
|
|||
html += u'<span style="display: none" class="attachRequiredField">%s</span>' % conditional_escape(i)
|
||||
required_str = u'Please fill in %s to attach a new file' % conditional_escape(self.required_label)
|
||||
html += u'<span style="display: none" class="attachDisabledLabel">%s</span>' % conditional_escape(required_str)
|
||||
html += u'<input type="button" class="addAttachmentWidget" value="%s" />' % conditional_escape(self.label)
|
||||
html += u'<input type="button" class="addAttachmentWidget btn btn-primary btn-sm" value="%s" />' % conditional_escape(self.label)
|
||||
return mark_safe(html)
|
||||
|
||||
|
||||
|
@ -71,8 +67,8 @@ class ShowAttachmentsWidget(Widget):
|
|||
|
||||
def render(self, name, value, attrs=None):
|
||||
html = u'<div id="id_%s">' % name
|
||||
html += u'<span style="display: none" class="showAttachmentsEmpty">No files attached</span>'
|
||||
html += u'<div class="attachedFiles">'
|
||||
html += u'<span style="display: none" class="showAttachmentsEmpty form-control" style="height: auto; min-height: 34px;">No files attached</span>'
|
||||
html += u'<div class="attachedFiles form-control" style="height: auto; min-height: 34px;">'
|
||||
if value and isinstance(value, QuerySet):
|
||||
for attachment in value:
|
||||
html += u'<a class="initialAttach" href="%s%s">%s</a><br />' % (settings.LIAISON_ATTACH_URL, conditional_escape(attachment.external_url), conditional_escape(attachment.title))
|
||||
|
@ -80,32 +76,3 @@ class ShowAttachmentsWidget(Widget):
|
|||
html += u'No files attached'
|
||||
html += u'</div></div>'
|
||||
return mark_safe(html)
|
||||
|
||||
|
||||
class RelatedLiaisonWidget(TextInput):
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
if not value:
|
||||
value = ''
|
||||
title = ''
|
||||
noliaison = 'inline'
|
||||
deselect = 'none'
|
||||
else:
|
||||
liaison = LiaisonStatement.objects.get(pk=value)
|
||||
title = liaison.title
|
||||
if not title:
|
||||
attachments = liaison.attachments.all()
|
||||
if attachments:
|
||||
title = attachments[0].title
|
||||
else:
|
||||
title = 'Liaison #%s' % liaison.pk
|
||||
noliaison = 'none'
|
||||
deselect = 'inline'
|
||||
html = u'<span class="noRelated" style="display: %s;">No liaison selected</span>' % conditional_escape(noliaison)
|
||||
html += u'<span class="relatedLiaisonWidgetTitle">%s</span>' % conditional_escape(title)
|
||||
html += u'<input type="hidden" name="%s" class="relatedLiaisonWidgetValue" value="%s" /> ' % (conditional_escape(name), conditional_escape(value))
|
||||
html += u'<span style="display: none;" class="listURL">%s</span> ' % urlreverse('ajax_liaison_list')
|
||||
html += u'<div style="display: none;" class="relatedLiaisonWidgetDialog" id="related-dialog" title="Select a liaison"></div> '
|
||||
html += '<input type="button" id="id_%s" value="Select liaison" /> ' % conditional_escape(name)
|
||||
html += '<input type="button" style="display: %s;" id="id_no_%s" value="Deselect liaison" />' % (conditional_escape(deselect), conditional_escape(name))
|
||||
return mark_safe(html)
|
||||
|
|
|
@ -18,13 +18,13 @@ class MailingListTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q(".group-archives a:contains(\"%s\")" % group.acronym)), 0)
|
||||
self.assertEqual(len(q(".content-wrapper a:contains(\"%s\")" % group.acronym)), 0)
|
||||
|
||||
# successful get
|
||||
group.list_archive = "https://example.com/foo"
|
||||
group.save()
|
||||
r = self.client.get(url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q(".group-archives a:contains(\"%s\")" % group.acronym)), 1)
|
||||
self.assertEqual(len(q(".content-wrapper a:contains(\"%s\")" % group.acronym)), 1)
|
||||
|
||||
|
||||
|
|
|
@ -39,19 +39,22 @@ class MeetingTests(TestCase):
|
|||
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||
slot = TimeSlot.objects.get(scheduledsession__session=session)
|
||||
|
||||
time_interval = "%s-%s" % (slot.time.strftime("%H%M"), (slot.time + slot.duration).strftime("%H%M"))
|
||||
time_interval = "%s-%s" % (slot.time.strftime("%H:%M").lstrip("0"), (slot.time + slot.duration).strftime("%H:%M").lstrip("0"))
|
||||
|
||||
# plain
|
||||
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number)))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
agenda_content = q("#agenda").html()
|
||||
agenda_content = q(".content-wrapper").html()
|
||||
self.assertTrue(session.group.acronym in agenda_content)
|
||||
self.assertTrue(session.group.name in agenda_content)
|
||||
self.assertTrue(session.group.parent.acronym.upper() in agenda_content)
|
||||
self.assertTrue(slot.location.name in agenda_content)
|
||||
self.assertTrue(time_interval in agenda_content)
|
||||
|
||||
# the rest of the results don't have as nicely formatted times
|
||||
time_interval = time_interval.replace(":", "")
|
||||
|
||||
# mobile
|
||||
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number)),
|
||||
{ '_testiphone': "1" })
|
||||
|
@ -125,12 +128,11 @@ class MeetingTests(TestCase):
|
|||
|
||||
r = self.client.get(urlreverse("ietf.meeting.views.materials", kwargs=dict(meeting_num=meeting.number)))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
#debug.show('r.content')
|
||||
q = PyQuery(r.content)
|
||||
row = q('.ietf-materials b:contains("%s")' % str(session.group.acronym.upper())).closest("tr")
|
||||
self.assertTrue(row.find("a:contains(\"Agenda\")"))
|
||||
self.assertTrue(row.find("a:contains(\"Minutes\")"))
|
||||
self.assertTrue(row.find("a:contains(\"Slideshow\")"))
|
||||
row = q('.content-wrapper td:contains("%s")' % str(session.group.acronym)).closest("tr")
|
||||
self.assertTrue(row.find('a:contains("Agenda")'))
|
||||
self.assertTrue(row.find('a:contains("Minutes")'))
|
||||
self.assertTrue(row.find('a:contains("Slideshow")'))
|
||||
|
||||
# FIXME: missing tests of .pdf/.tar generation (some code can
|
||||
# probably be lifted from similar tests in iesg/tests.py)
|
||||
|
|
|
@ -9,8 +9,7 @@ from ietf.meeting import ajax
|
|||
urlpatterns = patterns('',
|
||||
(r'^(?P<meeting_num>\d+)/materials.html$', views.materials),
|
||||
(r'^agenda/$', views.agenda),
|
||||
(r'^(?P<base>agenda-utc)(?P<ext>.html)?$', views.agenda),
|
||||
(r'^agenda(?P<ext>.html)?$', views.agenda),
|
||||
(r'^agenda(-utc)?(?P<ext>.html)?$', views.agenda),
|
||||
(r'^agenda(?P<ext>.txt)$', views.agenda),
|
||||
(r'^agenda(?P<ext>.csv)$', views.agenda),
|
||||
(r'^agenda/edit$', views.edit_agenda),
|
||||
|
@ -28,8 +27,7 @@ urlpatterns = patterns('',
|
|||
(r'^(?P<num>\d+)/agenda/(?P<owner>[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P<name>[A-Za-z0-9-:_]+)/sessions.json$', ajax.scheduledsessions_json),
|
||||
(r'^(?P<num>\d+)/agenda/(?P<owner>[A-Za-z0-9-.+_]+@[A-Za-z0-9._]+)/(?P<name>[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl),
|
||||
(r'^(?P<num>\d+)/agenda/edit$', views.edit_agenda),
|
||||
(r'^(?P<num>\d+)/agenda(?P<ext>.html)?/?$', views.agenda),
|
||||
(r'^(?P<num>\d+)/(?P<base>agenda-utc)(?P<ext>.html)?/?$', views.agenda),
|
||||
(r'^(?P<num>\d+)/agenda(-utc)?(?P<ext>.html)?/?$', views.agenda),
|
||||
(r'^(?P<num>\d+)/requests.html$', RedirectView.as_view(url='/meeting/%(num)s/requests', permanent=True)),
|
||||
(r'^(?P<num>\d+)/requests$', views.meeting_requests),
|
||||
(r'^(?P<num>\d+)/agenda(?P<ext>.txt)$', views.agenda),
|
||||
|
|
220
ietf/name/migrations/0030_fix_ipr_none_selected_choice.py
Normal file
220
ietf/name/migrations/0030_fix_ipr_none_selected_choice.py
Normal file
|
@ -0,0 +1,220 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import DataMigration
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
orm.IprLicenseTypeName.objects.filter(slug="none-selected").update(desc="[None selected]")
|
||||
|
||||
def backwards(self, orm):
|
||||
orm.IprLicenseTypeName.objects.filter(slug="none-selected").update(desc="")
|
||||
|
||||
models = {
|
||||
u'name.ballotpositionname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
|
||||
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.dbtemplatetypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.docrelationshipname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.docremindertypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.draftsubmissionstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['name.DraftSubmissionStateName']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.feedbacktypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'FeedbackTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.groupmilestonestatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.iprdisclosurestatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IprDisclosureStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.ipreventtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IprEventTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.iprlicensetypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IprLicenseTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.nomineepositionstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.rolename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.roomresourcename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['name']
|
||||
symmetrical = True
|
|
@ -11,13 +11,13 @@ from ietf.dbtemplate.forms import DBTemplateForm
|
|||
from ietf.group.models import Group, Role
|
||||
from ietf.ietfauth.utils import role_required
|
||||
from ietf.name.models import RoleName, FeedbackTypeName, NomineePositionStateName
|
||||
from ietf.nomcom.models import ( NomCom, Nomination, Nominee, NomineePosition,
|
||||
from ietf.nomcom.models import ( NomCom, Nomination, Nominee, NomineePosition,
|
||||
Position, Feedback, ReminderDates )
|
||||
from ietf.nomcom.utils import (NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE,
|
||||
get_user_email, validate_private_key, validate_public_key,
|
||||
get_or_create_nominee, create_feedback_email)
|
||||
from ietf.person.models import Email
|
||||
from ietf.person.fields import AutocompletedEmailField
|
||||
from ietf.person.fields import SearchableEmailField
|
||||
from ietf.utils.fields import MultiEmailField
|
||||
from ietf.utils.mail import send_mail
|
||||
|
||||
|
@ -207,62 +207,6 @@ class EditMembersFormPreview(FormPreview):
|
|||
return redirect('nomcom_edit_members', year=self.year)
|
||||
|
||||
|
||||
class EditChairForm(BaseNomcomForm, forms.Form):
|
||||
|
||||
chair = forms.EmailField(label="Chair email", required=False,
|
||||
widget=forms.TextInput(attrs={'size': '40'}))
|
||||
|
||||
fieldsets = [('Chair info', ('chair',))]
|
||||
|
||||
|
||||
class EditChairFormPreview(FormPreview):
|
||||
form_template = 'nomcom/edit_chair.html'
|
||||
preview_template = 'nomcom/edit_chair_preview.html'
|
||||
|
||||
@method_decorator(role_required("Secretariat"))
|
||||
def __call__(self, request, *args, **kwargs):
|
||||
year = kwargs['year']
|
||||
group = get_nomcom_group_or_404(year)
|
||||
self.state['group'] = group
|
||||
self.state['rolodex_url'] = ROLODEX_URL
|
||||
self.group = group
|
||||
self.year = year
|
||||
|
||||
return super(EditChairFormPreview, self).__call__(request, *args, **kwargs)
|
||||
|
||||
def get_initial(self, request):
|
||||
chair = self.group.get_chair()
|
||||
if chair:
|
||||
return { "chair": chair.email.address }
|
||||
return {}
|
||||
|
||||
def process_preview(self, request, form, context):
|
||||
chair_email = form.cleaned_data['chair']
|
||||
try:
|
||||
chair_email_obj = Email.objects.get(address=chair_email)
|
||||
chair_person = chair_email_obj.person
|
||||
except Email.DoesNotExist:
|
||||
chair_person = None
|
||||
chair_email_obj = None
|
||||
chair_info = {'email': chair_email,
|
||||
'email_obj': chair_email_obj,
|
||||
'person': chair_person}
|
||||
|
||||
self.state.update({'chair_info': chair_info})
|
||||
|
||||
def done(self, request, cleaned_data):
|
||||
chair_info = self.state['chair_info']
|
||||
chair_exclude = self.group.role_set.filter(name__slug='chair').exclude(email__address=chair_info['email'])
|
||||
chair_exclude.delete()
|
||||
if chair_info['email_obj'] and chair_info['person']:
|
||||
Role.objects.get_or_create(name=RoleName.objects.get(slug="chair"),
|
||||
group=self.group,
|
||||
person=chair_info['person'],
|
||||
email=chair_info['email_obj'])
|
||||
|
||||
return redirect('nomcom_edit_chair', year=self.year)
|
||||
|
||||
|
||||
class EditNomcomForm(BaseNomcomForm, forms.ModelForm):
|
||||
|
||||
fieldsets = [('Edit nomcom settings', ('public_key', 'initial_text',
|
||||
|
@ -378,7 +322,7 @@ class MergeForm(BaseNomcomForm, forms.Form):
|
|||
|
||||
|
||||
class NominateForm(BaseNomcomForm, forms.ModelForm):
|
||||
comments = forms.CharField(label="Candidate's Qualifications for the Position:",
|
||||
comments = forms.CharField(label="Candidate's qualifications for the position",
|
||||
widget=forms.Textarea())
|
||||
confirmation = forms.BooleanField(label='Email comments back to me as confirmation',
|
||||
help_text="If you want to get a confirmation mail containing your feedback in cleartext, \
|
||||
|
@ -400,6 +344,7 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
|
|||
'candidate_email', 'candidate_phone',
|
||||
'comments']
|
||||
|
||||
self.fields['nominator_email'].label = 'Nominator email'
|
||||
if self.nomcom:
|
||||
self.fields['position'].queryset = Position.objects.get_by_nomcom(self.nomcom).opened()
|
||||
self.fields['comments'].help_text = self.nomcom.initial_text
|
||||
|
@ -461,7 +406,7 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
|
|||
# send receipt email to nominator
|
||||
if confirmation:
|
||||
if author:
|
||||
subject = 'Nomination Receipt'
|
||||
subject = 'Nomination receipt'
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = author.address
|
||||
context = {'nominee': nominee.email.person.name,
|
||||
|
@ -479,15 +424,15 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
|
|||
|
||||
|
||||
class FeedbackForm(BaseNomcomForm, forms.ModelForm):
|
||||
position_name = forms.CharField(label='position',
|
||||
position_name = forms.CharField(label='Position',
|
||||
widget=forms.TextInput(attrs={'size': '40'}))
|
||||
nominee_name = forms.CharField(label='nominee name',
|
||||
nominee_name = forms.CharField(label='Nominee name',
|
||||
widget=forms.TextInput(attrs={'size': '40'}))
|
||||
nominee_email = forms.CharField(label='nominee email',
|
||||
nominee_email = forms.CharField(label='Nominee email',
|
||||
widget=forms.TextInput(attrs={'size': '40'}))
|
||||
nominator_email = forms.CharField(label='commenter email')
|
||||
nominator_email = forms.CharField(label='Commenter email')
|
||||
|
||||
comments = forms.CharField(label='Comments on this candidate',
|
||||
comments = forms.CharField(label='Comments on this nominee',
|
||||
widget=forms.Textarea())
|
||||
confirmation = forms.BooleanField(label='Email comments back to me as confirmation',
|
||||
help_text="If you want to get a confirmation mail containing your feedback in cleartext, \
|
||||
|
@ -656,7 +601,7 @@ class PositionForm(BaseNomcomForm, forms.ModelForm):
|
|||
fieldsets = [('Position', ('name', 'description',
|
||||
'is_open', 'incumbent'))]
|
||||
|
||||
incumbent = AutocompletedEmailField(required=False)
|
||||
incumbent = SearchableEmailField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Position
|
||||
|
|
|
@ -38,7 +38,7 @@ class NomCom(models.Model):
|
|||
upload_to=upload_path_handler, blank=True, null=True)
|
||||
|
||||
group = models.ForeignKey(Group)
|
||||
send_questionnaire = models.BooleanField(verbose_name='Send questionnaires automatically"', default=False,
|
||||
send_questionnaire = models.BooleanField(verbose_name='Send questionnaires automatically', default=False,
|
||||
help_text='If you check this box, questionnaires are sent automatically after nominations')
|
||||
reminder_interval = models.PositiveIntegerField(help_text='If the nomcom user sets the interval field then a cron command will \
|
||||
send reminders to the nominees who have not responded using \
|
||||
|
|
|
@ -41,12 +41,8 @@ def add_num_nominations(user, position, nominee):
|
|||
nominees__in=[nominee],
|
||||
author=author,
|
||||
type='comment').count()
|
||||
if count:
|
||||
mark = """<span style="white-space: pre; color: red;">*</span>"""
|
||||
else:
|
||||
mark = """<span style="white-space: pre;"> </span> """
|
||||
|
||||
return '<span title="%d earlier comments from you on %s as %s">%s</span> ' % (count, nominee.email.address, position, mark)
|
||||
return '<span class="badge" title="%d earlier comments from you on %s as %s">%s</span> ' % (count, nominee.email.address, position, count)
|
||||
|
||||
|
||||
@register.filter
|
||||
|
@ -76,7 +72,7 @@ def decrypt(string, request, year, plain=False):
|
|||
code, out, error = pipe(command % (settings.OPENSSL_COMMAND,
|
||||
encrypted_file.name), key)
|
||||
if code != 0:
|
||||
log("openssl error: %s:\n Error %s: %s" %(command, code, error))
|
||||
log("openssl error: %s:\n Error %s: %s" %(command, code, error))
|
||||
|
||||
os.unlink(encrypted_file.name)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: UTF-8-No-BOM -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
import tempfile
|
||||
import datetime
|
||||
|
||||
|
@ -22,7 +22,7 @@ from ietf.nomcom.test_data import nomcom_test_data, generate_cert, check_comment
|
|||
from ietf.nomcom.models import NomineePosition, Position, Nominee, \
|
||||
NomineePositionStateName, Feedback, FeedbackTypeName, \
|
||||
Nomination
|
||||
from ietf.nomcom.forms import EditChairForm, EditChairFormPreview, EditMembersForm
|
||||
from ietf.nomcom.forms import EditMembersForm, EditMembersFormPreview
|
||||
from ietf.nomcom.utils import get_nomcom_by_year, get_or_create_nominee
|
||||
from ietf.nomcom.management.commands.send_reminders import Command, is_time_to_send
|
||||
|
||||
|
@ -54,7 +54,6 @@ class NomcomViewsTest(TestCase):
|
|||
self.private_index_url = reverse('nomcom_private_index', kwargs={'year': self.year})
|
||||
self.private_merge_url = reverse('nomcom_private_merge', kwargs={'year': self.year})
|
||||
self.edit_members_url = reverse('nomcom_edit_members', kwargs={'year': self.year})
|
||||
self.edit_chair_url = reverse('nomcom_edit_chair', kwargs={'year': self.year})
|
||||
self.edit_nomcom_url = reverse('nomcom_edit_nomcom', kwargs={'year': self.year})
|
||||
self.private_nominate_url = reverse('nomcom_private_nominate', kwargs={'year': self.year})
|
||||
self.add_questionnaire_url = reverse('nomcom_private_questionnaire', kwargs={'year': self.year})
|
||||
|
@ -214,31 +213,36 @@ class NomcomViewsTest(TestCase):
|
|||
"primary_email": nominees[0]}
|
||||
response = self.client.post(self.private_merge_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-error")
|
||||
q = PyQuery(response.content)
|
||||
self.assertTrue(q("form .has-error"))
|
||||
|
||||
test_data = {"primary_email": nominees[0],
|
||||
"secondary_emails": ""}
|
||||
response = self.client.post(self.private_merge_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-error")
|
||||
q = PyQuery(response.content)
|
||||
self.assertTrue(q("form .has-error"))
|
||||
|
||||
test_data = {"primary_email": "",
|
||||
"secondary_emails": nominees[0]}
|
||||
response = self.client.post(self.private_merge_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-error")
|
||||
q = PyQuery(response.content)
|
||||
self.assertTrue(q("form .has-error"))
|
||||
|
||||
test_data = {"primary_email": "unknown@example.com",
|
||||
"secondary_emails": nominees[0]}
|
||||
response = self.client.post(self.private_merge_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-error")
|
||||
q = PyQuery(response.content)
|
||||
self.assertTrue(q("form .has-error"))
|
||||
|
||||
test_data = {"primary_email": nominees[0],
|
||||
"secondary_emails": "unknown@example.com"}
|
||||
response = self.client.post(self.private_merge_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-error")
|
||||
q = PyQuery(response.content)
|
||||
self.assertTrue(q("form .has-error"))
|
||||
|
||||
test_data = {"secondary_emails": """%s,
|
||||
%s,
|
||||
|
@ -247,7 +251,7 @@ class NomcomViewsTest(TestCase):
|
|||
|
||||
response = self.client.post(self.private_merge_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-success")
|
||||
self.assertContains(response, "alert-success")
|
||||
|
||||
self.assertEqual(Nominee.objects.filter(email__address=nominees[1],
|
||||
duplicated__isnull=False).count(), 1)
|
||||
|
@ -293,7 +297,7 @@ class NomcomViewsTest(TestCase):
|
|||
# preview
|
||||
self.client.post(self.edit_members_url, test_data)
|
||||
|
||||
hash = EditChairFormPreview(EditChairForm).security_hash(None, EditMembersForm(test_data))
|
||||
hash = EditMembersFormPreview(EditMembersForm).security_hash(None, EditMembersForm(test_data))
|
||||
test_data.update({'hash': hash, 'stage': 2})
|
||||
|
||||
# submit
|
||||
|
@ -318,33 +322,6 @@ class NomcomViewsTest(TestCase):
|
|||
self.check_url_status(self.private_index_url, 403)
|
||||
self.client.logout()
|
||||
|
||||
def change_chair(self, user):
|
||||
test_data = {'chair': '%s%s' % (user, EMAIL_DOMAIN),
|
||||
'stage': 1}
|
||||
# preview
|
||||
self.client.post(self.edit_chair_url, test_data)
|
||||
|
||||
hash = EditChairFormPreview(EditChairForm).security_hash(None, EditChairForm(test_data))
|
||||
test_data.update({'hash': hash, 'stage': 2})
|
||||
|
||||
# submit
|
||||
self.client.post(self.edit_chair_url, test_data)
|
||||
|
||||
def test_edit_chair_view(self):
|
||||
self.access_secretariat_url(self.edit_chair_url)
|
||||
self.change_chair(COMMUNITY_USER)
|
||||
|
||||
# check chair actions
|
||||
self.client.login(username=COMMUNITY_USER,password=COMMUNITY_USER+"+password")
|
||||
self.check_url_status(self.edit_members_url, 200)
|
||||
self.check_url_status(self.edit_nomcom_url, 200)
|
||||
self.client.logout()
|
||||
|
||||
# revert edit nomcom chair
|
||||
login_testing_unauthorized(self, SECRETARIAT_USER, self.edit_chair_url)
|
||||
self.change_chair(CHAIR_USER)
|
||||
self.client.logout()
|
||||
|
||||
def test_edit_nomcom_view(self):
|
||||
r = self.access_chair_url(self.edit_nomcom_url)
|
||||
q = PyQuery(r.content)
|
||||
|
@ -433,7 +410,8 @@ class NomcomViewsTest(TestCase):
|
|||
|
||||
nomcom = get_nomcom_by_year(self.year)
|
||||
if not nomcom.public_key:
|
||||
self.assertNotContains(response, "nominateform")
|
||||
q = PyQuery(response.content)
|
||||
self.assertEqual(len(q("#nominate-form")), 0)
|
||||
|
||||
# save the cert file in tmp
|
||||
nomcom.public_key.storage.location = tempfile.gettempdir()
|
||||
|
@ -441,7 +419,8 @@ class NomcomViewsTest(TestCase):
|
|||
|
||||
response = self.client.get(nominate_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "nominateform")
|
||||
q = PyQuery(response.content)
|
||||
self.assertEqual(len(q("#nominate-form")), 1)
|
||||
|
||||
position = Position.objects.get(name=position_name)
|
||||
candidate_email = nominee_email
|
||||
|
@ -459,7 +438,8 @@ class NomcomViewsTest(TestCase):
|
|||
|
||||
response = self.client.post(nominate_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-success")
|
||||
q = PyQuery(response.content)
|
||||
self.assertContains(response, "alert-success")
|
||||
|
||||
# check objects
|
||||
email = Email.objects.get(address=candidate_email)
|
||||
|
@ -526,7 +506,7 @@ class NomcomViewsTest(TestCase):
|
|||
response = self.client.post(self.add_questionnaire_url, test_data)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-success")
|
||||
self.assertContains(response, "alert-success")
|
||||
|
||||
## check objects
|
||||
feedback = Feedback.objects.filter(positions__in=[position],
|
||||
|
@ -597,17 +577,18 @@ class NomcomViewsTest(TestCase):
|
|||
nominee_position = NomineePosition.objects.get(nominee=nominee,
|
||||
position=position)
|
||||
state = nominee_position.state
|
||||
if not state.slug == 'accepted':
|
||||
if state.slug != 'accepted':
|
||||
response = self.client.post(feedback_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-error")
|
||||
q = PyQuery(response.content)
|
||||
self.assertTrue(q("form .has-error"))
|
||||
# accept nomination
|
||||
nominee_position.state = NomineePositionStateName.objects.get(slug='accepted')
|
||||
nominee_position.save()
|
||||
|
||||
response = self.client.post(feedback_url, test_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "info-message-success")
|
||||
self.assertContains(response, "alert-success")
|
||||
|
||||
## check objects
|
||||
feedback = Feedback.objects.filter(positions__in=[position],
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
from django.conf.urls import patterns, url
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from ietf.nomcom.forms import ( EditChairForm, EditChairFormPreview,
|
||||
EditMembersForm, EditMembersFormPreview )
|
||||
from ietf.nomcom.forms import EditMembersForm, EditMembersFormPreview
|
||||
|
||||
urlpatterns = patterns('ietf.nomcom.views',
|
||||
url(r'^$', 'index'),
|
||||
|
@ -22,7 +21,6 @@ urlpatterns = patterns('ietf.nomcom.views',
|
|||
# url(r'^(?P<year>\d{4})/private/send-reminder-mail/$', RedirectView.as_view(url=reverse_lazy('nomcom_send_reminder_mail',kwargs={'year':year,'type':'accept'}))),
|
||||
url(r'^(?P<year>\d{4})/private/send-reminder-mail/(?P<type>\w+)/$', 'send_reminder_mail', name='nomcom_send_reminder_mail'),
|
||||
url(r'^(?P<year>\d{4})/private/edit-members/$', EditMembersFormPreview(EditMembersForm), name='nomcom_edit_members'),
|
||||
url(r'^(?P<year>\d{4})/private/edit-chair/$', EditChairFormPreview(EditChairForm), name='nomcom_edit_chair'),
|
||||
url(r'^(?P<year>\d{4})/private/edit-nomcom/$', 'edit_nomcom', name='nomcom_edit_nomcom'),
|
||||
url(r'^(?P<year>\d{4})/private/delete-nomcom/$', 'delete_nomcom', name='nomcom_delete_nomcom'),
|
||||
url(r'^deleted/$', TemplateView.as_view(template_name='nomcom/deleted.html'), name='nomcom_deleted'),
|
||||
|
|
|
@ -56,7 +56,7 @@ def index(request):
|
|||
nomcom.ann_url = None
|
||||
return render_to_response('nomcom/index.html',
|
||||
{'nomcom_list': nomcom_list,}, RequestContext(request))
|
||||
|
||||
|
||||
|
||||
def year_index(request, year):
|
||||
nomcom = get_nomcom_by_year(year)
|
||||
|
@ -70,21 +70,21 @@ def year_index(request, year):
|
|||
|
||||
def announcements(request):
|
||||
address_re = re.compile("<.*>")
|
||||
|
||||
|
||||
nomcoms = Group.objects.filter(type="nomcom")
|
||||
|
||||
regimes = []
|
||||
|
||||
|
||||
for n in nomcoms:
|
||||
e = GroupEvent.objects.filter(group=n, type="changed_state", changestategroupevent__state="active").order_by('time')[:1]
|
||||
n.start_year = e[0].time.year if e else 0
|
||||
e = GroupEvent.objects.filter(group=n, type="changed_state", changestategroupevent__state="conclude").order_by('time')[:1]
|
||||
n.end_year = e[0].time.year if e else n.start_year + 1
|
||||
|
||||
r = n.role_set.select_related().filter(name="chair")
|
||||
chair = None
|
||||
if r:
|
||||
chair = r[0]
|
||||
r = n.role_set.select_related().filter(name="chair")
|
||||
chair = None
|
||||
if r:
|
||||
chair = r[0]
|
||||
|
||||
announcements = Message.objects.filter(related_groups=n).order_by('-time')
|
||||
for a in announcements:
|
||||
|
@ -311,8 +311,8 @@ def nominate(request, year, public):
|
|||
template = 'nomcom/private_nominate.html'
|
||||
|
||||
if not has_publickey:
|
||||
message = ('warning', "This Nomcom is not yet accepting nominations")
|
||||
return render_to_response(template,
|
||||
message = ('warning', "This Nomcom is not yet accepting nominations")
|
||||
return render_to_response(template,
|
||||
{'message': message,
|
||||
'nomcom': nomcom,
|
||||
'year': year,
|
||||
|
@ -361,17 +361,19 @@ def feedback(request, year, public):
|
|||
positions = Position.objects.get_by_nomcom(nomcom=nomcom).opened()
|
||||
|
||||
if public:
|
||||
template = 'nomcom/public_feedback.html'
|
||||
base_template = "nomcom/nomcom_public_base.html"
|
||||
else:
|
||||
template = 'nomcom/private_feedback.html'
|
||||
base_template = "nomcom/nomcom_private_base.html"
|
||||
|
||||
if not has_publickey:
|
||||
message = ('warning', "This Nomcom is not yet accepting comments")
|
||||
return render_to_response(template,
|
||||
{'message': message,
|
||||
'nomcom': nomcom,
|
||||
'year': year,
|
||||
'selected': 'feedback'}, RequestContext(request))
|
||||
return render(request, 'nomcom/feedback.html', {
|
||||
'message': message,
|
||||
'nomcom': nomcom,
|
||||
'year': year,
|
||||
'selected': 'feedback',
|
||||
'base_template': base_template
|
||||
})
|
||||
|
||||
message = None
|
||||
if request.method == 'POST':
|
||||
|
@ -385,14 +387,16 @@ def feedback(request, year, public):
|
|||
form = FeedbackForm(nomcom=nomcom, user=request.user, public=public,
|
||||
position=position, nominee=nominee)
|
||||
|
||||
return render_to_response(template,
|
||||
{'form': form,
|
||||
'message': message,
|
||||
'nomcom': nomcom,
|
||||
'year': year,
|
||||
'positions': positions,
|
||||
'submit_disabled': submit_disabled,
|
||||
'selected': 'feedback'}, RequestContext(request))
|
||||
return render(request, 'nomcom/feedback.html', {
|
||||
'form': form,
|
||||
'message': message,
|
||||
'nomcom': nomcom,
|
||||
'year': year,
|
||||
'positions': positions,
|
||||
'submit_disabled': submit_disabled,
|
||||
'selected': 'feedback',
|
||||
'base_template': base_template
|
||||
})
|
||||
|
||||
|
||||
@role_required("Nomcom Chair", "Nomcom Advisor")
|
||||
|
|
|
@ -8,7 +8,7 @@ import debug # pyflakes:ignore
|
|||
|
||||
from ietf.person.models import Email, Person
|
||||
|
||||
def tokeninput_id_name_json(objs):
|
||||
def select2_id_name_json(objs):
|
||||
def format_email(e):
|
||||
return escape(u"%s <%s>" % (e.person.name, e.address))
|
||||
def format_person(p):
|
||||
|
@ -16,63 +16,63 @@ def tokeninput_id_name_json(objs):
|
|||
|
||||
formatter = format_email if objs and isinstance(objs[0], Email) else format_person
|
||||
|
||||
return json.dumps([{ "id": o.pk, "name": formatter(o) } for o in objs])
|
||||
return json.dumps([{ "id": o.pk, "text": formatter(o) } for o in objs])
|
||||
|
||||
class AutocompletedPersonsField(forms.CharField):
|
||||
"""Tokenizing autocompleted multi-select field for choosing
|
||||
persons/emails or just persons using jquery.tokeninput.js.
|
||||
class SearchablePersonsField(forms.CharField):
|
||||
"""Server-based multi-select field for choosing
|
||||
persons/emails or just persons using select2.js.
|
||||
|
||||
The field operates on either Email or Person models. In the case
|
||||
of Email models, the person name is shown next to the email address.
|
||||
of Email models, the person name is shown next to the email
|
||||
address.
|
||||
|
||||
The field uses a comma-separated list of primary keys in a
|
||||
CharField element as its API, the tokeninput Javascript adds some
|
||||
selection magic on top of this so we have to pass it a JSON
|
||||
representation of ids and user-understandable labels."""
|
||||
CharField element as its API with some extra attributes used by
|
||||
the Javascript part."""
|
||||
|
||||
def __init__(self,
|
||||
max_entries=None, # max number of selected objs
|
||||
only_users=False, # only select persons who also have a user
|
||||
model=Person, # or Email
|
||||
hint_text="Type in name to search for person",
|
||||
hint_text="Type in name to search for person.",
|
||||
*args, **kwargs):
|
||||
kwargs["max_length"] = 1000
|
||||
self.max_entries = max_entries
|
||||
self.only_users = only_users
|
||||
self.model = model
|
||||
|
||||
super(AutocompletedPersonsField, self).__init__(*args, **kwargs)
|
||||
super(SearchablePersonsField, self).__init__(*args, **kwargs)
|
||||
|
||||
self.widget.attrs["class"] = "tokenized-field"
|
||||
self.widget.attrs["data-hint-text"] = hint_text
|
||||
self.widget.attrs["class"] = "select2-field"
|
||||
self.widget.attrs["data-placeholder"] = hint_text
|
||||
if self.max_entries != None:
|
||||
self.widget.attrs["data-max-entries"] = self.max_entries
|
||||
|
||||
def parse_tokenized_value(self, value):
|
||||
def parse_select2_value(self, value):
|
||||
return [x.strip() for x in value.split(",") if x.strip()]
|
||||
|
||||
def prepare_value(self, value):
|
||||
if not value:
|
||||
value = ""
|
||||
if isinstance(value, basestring):
|
||||
pks = self.parse_tokenized_value(value)
|
||||
pks = self.parse_select2_value(value)
|
||||
value = self.model.objects.filter(pk__in=pks).select_related("person")
|
||||
if isinstance(value, self.model):
|
||||
value = [value]
|
||||
|
||||
self.widget.attrs["data-pre"] = tokeninput_id_name_json(value)
|
||||
self.widget.attrs["data-pre"] = select2_id_name_json(value)
|
||||
|
||||
# doing this in the constructor is difficult because the URL
|
||||
# patterns may not have been fully constructed there yet
|
||||
self.widget.attrs["data-ajax-url"] = urlreverse("ajax_tokeninput_search", kwargs={ "model_name": self.model.__name__.lower() })
|
||||
self.widget.attrs["data-ajax-url"] = urlreverse("ajax_select2_search_person_email", kwargs={ "model_name": self.model.__name__.lower() })
|
||||
if self.only_users:
|
||||
self.widget.attrs["data-ajax-url"] += "?user=1" # require a Datatracker account
|
||||
|
||||
return ",".join(e.address for e in value)
|
||||
return u",".join(e.address for e in value)
|
||||
|
||||
def clean(self, value):
|
||||
value = super(AutocompletedPersonsField, self).clean(value)
|
||||
pks = self.parse_tokenized_value(value)
|
||||
value = super(SearchablePersonsField, self).clean(value)
|
||||
pks = self.parse_select2_value(value)
|
||||
|
||||
objs = self.model.objects.filter(pk__in=pks)
|
||||
if self.model == Email:
|
||||
|
@ -92,32 +92,32 @@ class AutocompletedPersonsField(forms.CharField):
|
|||
|
||||
return objs
|
||||
|
||||
class AutocompletedPersonField(AutocompletedPersonsField):
|
||||
"""Version of AutocompletedPersonsField specialized to a single object."""
|
||||
class SearchablePersonField(SearchablePersonsField):
|
||||
"""Version of SearchablePersonsField specialized to a single object."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["max_entries"] = 1
|
||||
super(AutocompletedPersonField, self).__init__(*args, **kwargs)
|
||||
super(SearchablePersonField, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
return super(AutocompletedPersonField, self).clean(value).first()
|
||||
return super(SearchablePersonField, self).clean(value).first()
|
||||
|
||||
|
||||
class AutocompletedEmailsField(AutocompletedPersonsField):
|
||||
"""Version of AutocompletedPersonsField with the defaults right for Emails."""
|
||||
class SearchableEmailsField(SearchablePersonsField):
|
||||
"""Version of SearchablePersonsField with the defaults right for Emails."""
|
||||
|
||||
def __init__(self, model=Email, hint_text="Type in name or email to search for person and email address",
|
||||
def __init__(self, model=Email, hint_text="Type in name or email to search for person and email address.",
|
||||
*args, **kwargs):
|
||||
super(AutocompletedEmailsField, self).__init__(model=model, hint_text=hint_text, *args, **kwargs)
|
||||
super(SearchableEmailsField, self).__init__(model=model, hint_text=hint_text, *args, **kwargs)
|
||||
|
||||
class AutocompletedEmailField(AutocompletedEmailsField):
|
||||
"""Version of AutocompletedEmailsField specialized to a single object."""
|
||||
class SearchableEmailField(SearchableEmailsField):
|
||||
"""Version of SearchableEmailsField specialized to a single object."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["max_entries"] = 1
|
||||
super(AutocompletedEmailField, self).__init__(*args, **kwargs)
|
||||
super(SearchableEmailField, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
return super(AutocompletedEmailField, self).clean(value).first()
|
||||
return super(SearchableEmailField, self).clean(value).first()
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class PersonTests(TestCase):
|
|||
draft = make_test_data()
|
||||
person = draft.ad
|
||||
|
||||
r = self.client.get(urlreverse("ietf.person.views.ajax_tokeninput_search", kwargs={ "model_name": "email"}), dict(q=person.name))
|
||||
r = self.client.get(urlreverse("ietf.person.views.ajax_select2_search", kwargs={ "model_name": "email"}), dict(q=person.name))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
data = json.loads(r.content)
|
||||
self.assertEqual(data[0]["id"], person.email_address())
|
||||
|
|
|
@ -2,6 +2,6 @@ from django.conf.urls import patterns
|
|||
from ietf.person import ajax
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^search/(?P<model_name>(person|email))/$', "ietf.person.views.ajax_tokeninput_search", None, 'ajax_tokeninput_search'),
|
||||
(r'^search/(?P<model_name>(person|email))/$', "ietf.person.views.ajax_select2_search", None, 'ajax_select2_search_person_email'),
|
||||
(r'^(?P<personid>[a-z0-9]+).json$', ajax.person_json),
|
||||
)
|
||||
|
|
|
@ -2,9 +2,9 @@ from django.http import HttpResponse
|
|||
from django.db.models import Q
|
||||
|
||||
from ietf.person.models import Email, Person
|
||||
from ietf.person.fields import tokeninput_id_name_json
|
||||
from ietf.person.fields import select2_id_name_json
|
||||
|
||||
def ajax_tokeninput_search(request, model_name):
|
||||
def ajax_select2_search(request, model_name):
|
||||
if model_name == "email":
|
||||
model = Email
|
||||
else:
|
||||
|
@ -39,6 +39,11 @@ def ajax_tokeninput_search(request, model_name):
|
|||
if only_users:
|
||||
objs = objs.exclude(user=None)
|
||||
|
||||
objs = objs.distinct()[:10]
|
||||
try:
|
||||
page = int(request.GET.get("p", 1)) - 1
|
||||
except ValueError:
|
||||
page = 0
|
||||
|
||||
return HttpResponse(tokeninput_id_name_json(objs), content_type='application/json')
|
||||
objs = objs.distinct()[page:page + 10]
|
||||
|
||||
return HttpResponse(select2_id_name_json(objs), content_type='application/json')
|
||||
|
|
|
@ -8,7 +8,7 @@ from ietf.doc.models import Document, DocAlias, State
|
|||
from ietf.name.models import IntendedStdLevelName, DocRelationshipName
|
||||
from ietf.group.models import Group
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.person.fields import AutocompletedEmailField
|
||||
from ietf.person.fields import SearchableEmailField
|
||||
from ietf.secr.groups.forms import get_person
|
||||
|
||||
|
||||
|
@ -132,7 +132,7 @@ class EditModelForm(forms.ModelForm):
|
|||
iesg_state = forms.ModelChoiceField(queryset=State.objects.filter(type='draft-iesg'),required=False)
|
||||
group = GroupModelChoiceField(required=True)
|
||||
review_by_rfc_editor = forms.BooleanField(required=False)
|
||||
shepherd = AutocompletedEmailField(required=False, only_users=True)
|
||||
shepherd = SearchableEmailField(required=False, only_users=True)
|
||||
|
||||
class Meta:
|
||||
model = Document
|
||||
|
|
|
@ -2,7 +2,7 @@ from django import forms
|
|||
|
||||
from ietf.group.models import Group
|
||||
from ietf.meeting.models import ResourceAssociation
|
||||
from ietf.person.fields import AutocompletedPersonsField
|
||||
from ietf.person.fields import SearchablePersonsField
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
|
@ -67,7 +67,7 @@ class SessionForm(forms.Form):
|
|||
wg_selector3 = forms.ChoiceField(choices=WG_CHOICES,required=False)
|
||||
third_session = forms.BooleanField(required=False)
|
||||
resources = forms.MultipleChoiceField(choices=[(x.pk,x.desc) for x in ResourceAssociation.objects.all()], widget=forms.CheckboxSelectMultiple,required=False)
|
||||
bethere = AutocompletedPersonsField(label="Must be present", required=False)
|
||||
bethere = SearchablePersonsField(label="Must be present", required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SessionForm, self).__init__(*args, **kwargs)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
{% block extrastyle %}{% endblock %}
|
||||
|
||||
{% block extrahead %}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/jquery-1.5.1.min.js"></script>
|
||||
<script type="text/javascript" src="/facelift/js/lib/jquery-1.11.1.min.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
|
||||
|
|
|
@ -7,9 +7,58 @@
|
|||
<link rel="stylesheet" type="text/css" href="{{ SECR_STATIC_URL }}css/jquery.ui.autocomplete.css" />
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/jquery-ui-1.8.1.custom.min.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
|
||||
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
|
||||
<script type="text/javascript" src="/js/tokenized-field.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2.css">
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2-bootstrap.css">
|
||||
<script src="/facelift/js/lib/select2-3.5.2.min.js"></script>
|
||||
<script>
|
||||
// this is copy-pasted from ietf.js, we should include that here too instead of this
|
||||
$(document).ready(function () {
|
||||
$(".select2-field").each(function () {
|
||||
var e = $(this);
|
||||
if (e.data("ajax-url")) {
|
||||
var maxEntries = e.data("max-entries");
|
||||
var multiple = maxEntries != 1;
|
||||
var prefetched = e.data("pre");
|
||||
e.select2({
|
||||
multiple: multiple,
|
||||
minimumInputLength: 2,
|
||||
width: "off",
|
||||
allowClear: true,
|
||||
maximumSelectionSize: maxEntries,
|
||||
ajax: {
|
||||
url: e.data("ajax-url"),
|
||||
dataType: "json",
|
||||
quietMillis: 250,
|
||||
data: function (term, page) {
|
||||
return {
|
||||
q: term,
|
||||
p: page
|
||||
};
|
||||
},
|
||||
results: function (results) {
|
||||
return {
|
||||
results: results,
|
||||
more: results.length == 10
|
||||
};
|
||||
}
|
||||
},
|
||||
escapeMarkup: function (m) {
|
||||
return m;
|
||||
},
|
||||
initSelection: function (element, cb) {
|
||||
if (!multiple && prefetched.length > 0)
|
||||
cb(prefetched[0]);
|
||||
else
|
||||
cb(prefetched);
|
||||
|
||||
},
|
||||
dropdownCssClass: "bigdrop"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
|
|
|
@ -5,9 +5,58 @@
|
|||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/sessions.js"></script>
|
||||
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
|
||||
<script type="text/javascript" src="/js/tokenized-field.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
|
||||
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2.css">
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2-bootstrap.css">
|
||||
<script src="/facelift/js/lib/select2-3.5.2.min.js"></script>
|
||||
<script>
|
||||
// this is copy-pasted from ietf.js, we should include that here too instead of this
|
||||
$(document).ready(function () {
|
||||
$(".select2-field").each(function () {
|
||||
var e = $(this);
|
||||
if (e.data("ajax-url")) {
|
||||
var maxEntries = e.data("max-entries");
|
||||
var multiple = maxEntries != 1;
|
||||
var prefetched = e.data("pre");
|
||||
e.select2({
|
||||
multiple: multiple,
|
||||
minimumInputLength: 2,
|
||||
width: "off",
|
||||
allowClear: true,
|
||||
maximumSelectionSize: maxEntries,
|
||||
ajax: {
|
||||
url: e.data("ajax-url"),
|
||||
dataType: "json",
|
||||
quietMillis: 250,
|
||||
data: function (term, page) {
|
||||
return {
|
||||
q: term,
|
||||
p: page
|
||||
};
|
||||
},
|
||||
results: function (results) {
|
||||
return {
|
||||
results: results,
|
||||
more: results.length == 10
|
||||
};
|
||||
}
|
||||
},
|
||||
escapeMarkup: function (m) {
|
||||
return m;
|
||||
},
|
||||
initSelection: function (element, cb) {
|
||||
if (!multiple && prefetched.length > 0)
|
||||
cb(prefetched[0]);
|
||||
else
|
||||
cb(prefetched);
|
||||
|
||||
},
|
||||
dropdownCssClass: "bigdrop"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
|
|
|
@ -5,9 +5,58 @@
|
|||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/sessions.js"></script>
|
||||
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
|
||||
<script type="text/javascript" src="/js/tokenized-field.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
|
||||
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2.css">
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2-bootstrap.css">
|
||||
<script src="/facelift/js/lib/select2-3.5.2.min.js"></script>
|
||||
<script>
|
||||
// this is copy-pasted from ietf.js, we should include that here too instead of this
|
||||
$(document).ready(function () {
|
||||
$(".select2-field").each(function () {
|
||||
var e = $(this);
|
||||
if (e.data("ajax-url")) {
|
||||
var maxEntries = e.data("max-entries");
|
||||
var multiple = maxEntries != 1;
|
||||
var prefetched = e.data("pre");
|
||||
e.select2({
|
||||
multiple: multiple,
|
||||
minimumInputLength: 2,
|
||||
width: "off",
|
||||
allowClear: true,
|
||||
maximumSelectionSize: maxEntries,
|
||||
ajax: {
|
||||
url: e.data("ajax-url"),
|
||||
dataType: "json",
|
||||
quietMillis: 250,
|
||||
data: function (term, page) {
|
||||
return {
|
||||
q: term,
|
||||
p: page
|
||||
};
|
||||
},
|
||||
results: function (results) {
|
||||
return {
|
||||
results: results,
|
||||
more: results.length == 10
|
||||
};
|
||||
}
|
||||
},
|
||||
escapeMarkup: function (m) {
|
||||
return m;
|
||||
},
|
||||
initSelection: function (element, cb) {
|
||||
if (!multiple && prefetched.length > 0)
|
||||
cb(prefetched[0]);
|
||||
else
|
||||
cb(prefetched);
|
||||
|
||||
},
|
||||
dropdownCssClass: "bigdrop"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
|
|
|
@ -14,7 +14,7 @@ except ImportError:
|
|||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# a place to put ajax logs if necessary.
|
||||
LOG_DIR = '/var/log/datatracker'
|
||||
LOG_DIR = '/var/log/datatracker'
|
||||
|
||||
import sys
|
||||
sys.path.append(os.path.abspath(BASE_DIR + "/.."))
|
||||
|
@ -63,9 +63,9 @@ DATABASES = {
|
|||
}
|
||||
|
||||
DATABASE_TEST_OPTIONS = {
|
||||
# Comment this out if your database doesn't support InnoDB
|
||||
'init_command': 'SET storage_engine=InnoDB',
|
||||
}
|
||||
# Comment this out if your database doesn't support InnoDB
|
||||
'init_command': 'SET storage_engine=InnoDB',
|
||||
}
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
|
@ -87,10 +87,11 @@ USE_I18N = False
|
|||
|
||||
USE_TZ = False
|
||||
|
||||
MEDIA_URL = 'http://www.ietf.org/'
|
||||
MEDIA_URL = '//www.ietf.org/'
|
||||
|
||||
STATIC_URL = "/"
|
||||
STATIC_ROOT = os.path.abspath(BASE_DIR + "/../static/")
|
||||
STATIC_URL = STATIC_ROOT + '/'
|
||||
|
||||
WSGI_APPLICATION = "ietf.wsgi.application"
|
||||
|
||||
|
@ -232,8 +233,34 @@ INSTALLED_APPS = (
|
|||
'ietf.secr.sreq',
|
||||
'ietf.nomcom',
|
||||
'ietf.dbtemplate',
|
||||
# FACELIFT: apps for facelift UI
|
||||
'widget_tweaks',
|
||||
'typogrify',
|
||||
'bootstrap3',
|
||||
)
|
||||
|
||||
# Settings for django-bootstrap3
|
||||
# See http://django-bootstrap3.readthedocs.org/en/latest/settings.html
|
||||
BOOTSTRAP3 = {
|
||||
# Label class to use in horizontal forms
|
||||
'horizontal_label_class': 'col-md-2',
|
||||
|
||||
# Field class to use in horiozntal forms
|
||||
'horizontal_field_class': 'col-md-10',
|
||||
|
||||
# Set HTML required attribute on required fields
|
||||
'set_required': True,
|
||||
|
||||
# Set placeholder attributes to label if no placeholder is provided
|
||||
'set_placeholder': False,
|
||||
|
||||
# Class to indicate required
|
||||
'form_required_class': 'bootstrap3-required',
|
||||
|
||||
# Class to indicate error
|
||||
'form_error_class': 'bootstrap3-error',
|
||||
}
|
||||
|
||||
INTERNAL_IPS = (
|
||||
# AMS servers
|
||||
'64.170.98.32',
|
||||
|
@ -292,22 +319,22 @@ INTERNET_DRAFT_ARCHIVE_DIR = '/a/www/www6s/draft-archive'
|
|||
MEETING_RECORDINGS_DIR = '/a/www/audio'
|
||||
|
||||
# Mailing list info URL for lists hosted on the IETF servers
|
||||
MAILING_LIST_INFO_URL = "https://www.ietf.org/mailman/listinfo/%(list_addr)s"
|
||||
MAILING_LIST_INFO_URL = "//www.ietf.org/mailman/listinfo/%(list_addr)s"
|
||||
|
||||
# Ideally, more of these would be local -- but since we don't support
|
||||
# versions right now, we'll point to external websites
|
||||
DOC_HREFS = {
|
||||
"charter": "http://www.ietf.org/charter/{doc.name}-{doc.rev}.txt",
|
||||
"draft": "http://www.ietf.org/archive/id/{doc.name}-{doc.rev}.txt",
|
||||
"slides": "http://www.ietf.org/slides/{doc.name}-{doc.rev}",
|
||||
"conflrev": "http://www.ietf.org/cr/{doc.name}-{doc.rev}.txt",
|
||||
"statchg": "http://www.ietf.org/sc/{doc.name}-{doc.rev}.txt",
|
||||
"charter": "//www.ietf.org/charter/{doc.name}-{doc.rev}.txt",
|
||||
"draft": "//www.ietf.org/archive/id/{doc.name}-{doc.rev}.txt",
|
||||
"slides": "//www.ietf.org/slides/{doc.name}-{doc.rev}",
|
||||
"conflrev": "//www.ietf.org/cr/{doc.name}-{doc.rev}.txt",
|
||||
"statchg": "//www.ietf.org/sc/{doc.name}-{doc.rev}.txt",
|
||||
}
|
||||
|
||||
MEETING_DOC_HREFS = {
|
||||
"agenda": "/meeting/{meeting}/agenda/{doc.group.acronym}/",
|
||||
"minutes": "http://www.ietf.org/proceedings/{meeting}/minutes/{doc.external_url}",
|
||||
"slides": "http://www.ietf.org/proceedings/{meeting}/slides/{doc.external_url}",
|
||||
"minutes": "//www.ietf.org/proceedings/{meeting}/minutes/{doc.external_url}",
|
||||
"slides": "//www.ietf.org/proceedings/{meeting}/slides/{doc.external_url}",
|
||||
"recording": "{doc.external_url}",
|
||||
}
|
||||
|
||||
|
@ -332,15 +359,15 @@ IANA_APPROVE_EMAIL = "drafts-approval@icann.org"
|
|||
|
||||
# Put real password in settings_local.py
|
||||
IANA_SYNC_PASSWORD = "secret"
|
||||
IANA_SYNC_CHANGES_URL = "https://datatracker.iana.org:4443/data-tracker/changes"
|
||||
IANA_SYNC_PROTOCOLS_URL = "http://www.iana.org/protocols/"
|
||||
IANA_SYNC_CHANGES_URL = "//datatracker.iana.org:4443/data-tracker/changes"
|
||||
IANA_SYNC_PROTOCOLS_URL = "//www.iana.org/protocols/"
|
||||
|
||||
RFC_TEXT_RSYNC_SOURCE="ftp.rfc-editor.org::rfcs-text-only"
|
||||
|
||||
RFC_EDITOR_SYNC_PASSWORD="secret"
|
||||
RFC_EDITOR_SYNC_NOTIFICATION_URL = "http://www.rfc-editor.org/parser/parser.php"
|
||||
RFC_EDITOR_QUEUE_URL = "http://www.rfc-editor.org/queue2.xml"
|
||||
RFC_EDITOR_INDEX_URL = "http://www.rfc-editor.org/rfc/rfc-index.xml"
|
||||
RFC_EDITOR_SYNC_NOTIFICATION_URL = "//www.rfc-editor.org/parser/parser.php"
|
||||
RFC_EDITOR_QUEUE_URL = "//www.rfc-editor.org/queue2.xml"
|
||||
RFC_EDITOR_INDEX_URL = "//www.rfc-editor.org/rfc/rfc-index.xml"
|
||||
|
||||
# Liaison Statement Tool settings
|
||||
LIAISON_UNIVERSAL_FROM = 'Liaison Statement Management Tool <lsmt@' + IETF_DOMAIN + '>'
|
||||
|
@ -376,7 +403,7 @@ INTERNET_DRAFT_DAYS_TO_EXPIRE = 185
|
|||
|
||||
IDSUBMIT_REPOSITORY_PATH = INTERNET_DRAFT_PATH
|
||||
IDSUBMIT_STAGING_PATH = '/a/www/www6s/staging/'
|
||||
IDSUBMIT_STAGING_URL = 'http://www.ietf.org/staging/'
|
||||
IDSUBMIT_STAGING_URL = '//www.ietf.org/staging/'
|
||||
IDSUBMIT_IDNITS_BINARY = '/a/www/ietf-datatracker/scripts/idnits'
|
||||
|
||||
IDSUBMIT_MAX_PLAIN_DRAFT_SIZE = 6291456 # Max size of the txt draft in bytes
|
||||
|
@ -410,7 +437,7 @@ TZDATA_ICS_PATH = BASE_DIR + '/../vzic/zoneinfo/'
|
|||
CHANGELOG_PATH = '/www/ietf-datatracker/web/changelog'
|
||||
|
||||
SECR_BLUE_SHEET_PATH = '/a/www/ietf-datatracker/documents/blue_sheet.rtf'
|
||||
SECR_BLUE_SHEET_URL = 'https://datatracker.ietf.org/documents/blue_sheet.rtf'
|
||||
SECR_BLUE_SHEET_URL = '//datatracker.ietf.org/documents/blue_sheet.rtf'
|
||||
SECR_INTERIM_LISTING_DIR = '/a/www/www6/meeting/interim'
|
||||
SECR_MAX_UPLOAD_SIZE = 40960000
|
||||
SECR_PROCEEDINGS_DIR = '/a/www/www6s/proceedings/'
|
||||
|
@ -447,9 +474,8 @@ BADNESS_MUCHTOOBIG = 500
|
|||
SELENIUM_TESTS = False
|
||||
SELENIUM_TESTS_ONLY = False
|
||||
|
||||
# Path to the email alias lists. Used by ietf.utils.aliases
|
||||
DRAFT_ALIASES_PATH = "/a/postfix/draft-aliases"
|
||||
DRAFT_VIRTUAL_PATH = "/a/postfix/draft-virtual"
|
||||
# Set debug apps in DEV_APPS settings_local
|
||||
DEV_APPS = ()
|
||||
DRAFT_VIRTUAL_DOMAIN = "virtual.ietf.org"
|
||||
|
||||
GROUP_ALIASES_PATH = "/a/postfix/group-aliases"
|
||||
|
@ -462,6 +488,9 @@ POSTCONFIRM_PATH = "/a/postconfirm/test-wrapper"
|
|||
# sensitive or site-specific changes. DO NOT commit settings_local.py to svn.
|
||||
from settings_local import * # pyflakes:ignore
|
||||
|
||||
# Add DEV_APPS to INSTALLED_APPS
|
||||
INSTALLED_APPS += DEV_APPS
|
||||
|
||||
# We provide a secret key only for test and development modes. It's
|
||||
# absolutely vital that django fails to start in production mode unless a
|
||||
# secret key has been provided elsewhere, not in this file which is
|
||||
|
|
|
@ -47,20 +47,20 @@ class UploadForm(forms.Form):
|
|||
ietf_monday = Meeting.get_ietf_monday()
|
||||
|
||||
if now.date() >= (first_cut_off-timedelta(days=settings.CUTOFF_WARNING_DAYS)) and now.date() < first_cut_off:
|
||||
self.cutoff_warning = ( 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) is %s at %02sh UTC.<br/>' % (first_cut_off, settings.CUTOFF_HOUR) +
|
||||
'The pre-meeting cut-off date for revisions to existing documents is %s at %02sh UTC.<br/>' % (second_cut_off, settings.CUTOFF_HOUR) )
|
||||
self.cutoff_warning = ( 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) is %s at %02sh UTC.' % (first_cut_off, settings.CUTOFF_HOUR) +
|
||||
'The pre-meeting cut-off date for revisions to existing documents is %s at %02sh UTC.' % (second_cut_off, settings.CUTOFF_HOUR) )
|
||||
elif now.date() >= first_cut_off and now.date() < second_cut_off: # We are in the first_cut_off
|
||||
if now.date() == first_cut_off and now.hour < settings.CUTOFF_HOUR:
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) is %s, at %02sh UTC. After that, you will not be able to submit a new document until %s, at %sh UTC' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, )
|
||||
else: # No 00 version allowed
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %sh UTC. You will not be able to submit a new document until %s, at %sh UTC.<br>You can still submit a version -01 or higher Internet-Draft until %sh UTC, %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, second_cut_off, )
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %sh UTC. You will not be able to submit a new document until %s, at %sh UTC. You can still submit a version -01 or higher Internet-Draft until %sh UTC, %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, second_cut_off, )
|
||||
self.in_first_cut_off = True
|
||||
elif now.date() >= second_cut_off and now.date() < ietf_monday:
|
||||
if now.date() == second_cut_off and now.hour < settings.CUTOFF_HOUR: # We are in the first_cut_off yet
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %02sh UTC. You will not be able to submit a new document until %s, at %02sh UTC.<br>The I-D submission tool will be shut down at %02sh UTC today, and reopened at %02sh UTC on %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %02sh UTC. You will not be able to submit a new document until %s, at %02sh UTC. The I-D submission tool will be shut down at %02sh UTC today, and reopened at %02sh UTC on %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.in_first_cut_off = True
|
||||
else: # Completely shut down of the tool
|
||||
self.cutoff_warning = 'The cut-off time for the I-D submission was %02dh UTC, %s.<br>The I-D submission tool will be reopened at %02dh local time at the IETF meeting location, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.cutoff_warning = 'The cut-off time for the I-D submission was %02dh UTC, %s. The I-D submission tool will be reopened at %02dh local time at the IETF meeting location, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday)
|
||||
self.shutdown = True
|
||||
|
||||
def clean_file(self, field_name, parser_class):
|
||||
|
@ -116,7 +116,7 @@ class UploadForm(forms.Form):
|
|||
# check existing
|
||||
existing = Submission.objects.filter(name=self.parsed_draft.filename, rev=self.parsed_draft.revision).exclude(state__in=("posted", "cancel"))
|
||||
if existing:
|
||||
raise forms.ValidationError(mark_safe('Submission with same name and revision is currently being processed. <a href="%s">Check the status here</a>' % urlreverse("submit_submission_status", kwargs={ 'submission_id': existing[0].pk })))
|
||||
raise forms.ValidationError(mark_safe('Submission with same name and revision is currently being processed. <a href="%s">Check the status here.</a>' % urlreverse("submit_submission_status", kwargs={ 'submission_id': existing[0].pk })))
|
||||
|
||||
# cut-off
|
||||
if self.parsed_draft.revision == '00' and self.in_first_cut_off:
|
||||
|
@ -239,7 +239,7 @@ class EditSubmissionForm(forms.ModelForm):
|
|||
pages = forms.IntegerField(required=True)
|
||||
abstract = forms.CharField(widget=forms.Textarea, required=True)
|
||||
|
||||
note = forms.CharField(label=mark_safe(u'Comment to<br/> the Secretariat'), widget=forms.Textarea, required=False)
|
||||
note = forms.CharField(label=mark_safe(u'Comment to the Secretariat'), widget=forms.Textarea, required=False)
|
||||
|
||||
class Meta:
|
||||
model = Submission
|
||||
|
|
|
@ -17,7 +17,7 @@ def show_submission_files(context, submission):
|
|||
exists = True
|
||||
elif submission.state_id == "posted":
|
||||
continue
|
||||
result.append({'name': '[%s version]' % ext[1:].upper(),
|
||||
result.append({'name': '%s' % ext[1:],
|
||||
'exists': exists,
|
||||
'url': '%s%s-%s%s' % (settings.IDSUBMIT_STAGING_URL, submission.name, submission.rev, ext)})
|
||||
return {'files': result}
|
||||
|
@ -27,15 +27,16 @@ def show_submission_files(context, submission):
|
|||
def two_pages_decorated_with_errors(submission, errors):
|
||||
pages = submission.first_two_pages or ''
|
||||
if 'rev' not in errors.keys():
|
||||
return mark_safe('<pre class="twopages">%s</pre>' % escape(pages))
|
||||
result = '<pre class="twopages">\n'
|
||||
return mark_safe('<pre>%s</pre>' % escape(pages))
|
||||
result = '<pre>\n'
|
||||
for line in pages.split('\n'):
|
||||
if line.find('%s-%s' % (submission.name, submission.rev)) > -1:
|
||||
result += '</pre><pre class="twopages" style="background: red;">'
|
||||
result += '<div class="bg-danger"><b>'
|
||||
result += escape(line)
|
||||
result += '\n'
|
||||
result += '</pre><pre class="twopages">\n'
|
||||
result += '</b></div>\n'
|
||||
else:
|
||||
result += escape(line)
|
||||
result += '\n'
|
||||
result += '</pre>pre>\n'
|
||||
return mark_safe(result)
|
||||
|
|
|
@ -89,7 +89,7 @@ class SubmitTests(TestCase):
|
|||
# check the page
|
||||
r = self.client.get(status_url)
|
||||
q = PyQuery(r.content)
|
||||
post_button = q('input[type=submit][value*="Post"]')
|
||||
post_button = q('[type=submit]:contains("Post")')
|
||||
self.assertEqual(len(post_button), 1)
|
||||
action = post_button.parents("form").find('input[type=hidden][name="action"]').val()
|
||||
|
||||
|
@ -142,7 +142,7 @@ class SubmitTests(TestCase):
|
|||
r = self.client.get(status_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
approve_button = q('input[type=submit][value*="Approve"]')
|
||||
approve_button = q('[type=submit]:contains("Approve")')
|
||||
self.assertEqual(len(approve_button), 1)
|
||||
|
||||
action = approve_button.parents("form").find('input[type=hidden][name="action"]').val()
|
||||
|
@ -237,7 +237,7 @@ class SubmitTests(TestCase):
|
|||
# go to confirm page
|
||||
r = self.client.get(confirm_url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('input[type=submit][value*="Confirm"]')), 1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Confirm")')), 1)
|
||||
|
||||
# confirm
|
||||
mailbox_before = len(outbox)
|
||||
|
@ -305,7 +305,7 @@ class SubmitTests(TestCase):
|
|||
# go to confirm page
|
||||
r = self.client.get(confirm_url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('input[type=submit][value*="Confirm"]')), 1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Confirm")')), 1)
|
||||
|
||||
# confirm
|
||||
mailbox_before = len(outbox)
|
||||
|
@ -363,10 +363,10 @@ class SubmitTests(TestCase):
|
|||
r = self.client.get(status_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
cancel_button = q('input[type=submit][value*="Cancel"]')
|
||||
cancel_button = q('[type=submit]:contains("Cancel")')
|
||||
self.assertEqual(len(cancel_button), 1)
|
||||
|
||||
action = cancel_button.parents("form").find("input[type=hidden][name=\"action\"]").val()
|
||||
action = cancel_button.parents("form").find('input[type=hidden][name="action"]').val()
|
||||
|
||||
# cancel
|
||||
r = self.client.post(status_url, dict(action=action))
|
||||
|
@ -385,7 +385,7 @@ class SubmitTests(TestCase):
|
|||
r = self.client.get(status_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
adjust_button = q('input[type=submit][value*="Adjust"]')
|
||||
adjust_button = q('[type=submit]:contains("Adjust")')
|
||||
self.assertEqual(len(adjust_button), 1)
|
||||
|
||||
action = adjust_button.parents("form").find('input[type=hidden][name="action"]').val()
|
||||
|
@ -447,7 +447,7 @@ class SubmitTests(TestCase):
|
|||
r = self.client.get(status_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
post_button = q('input[type=submit][value*="Force"]')
|
||||
post_button = q('[type=submit]:contains("Force")')
|
||||
self.assertEqual(len(post_button), 1)
|
||||
|
||||
action = post_button.parents("form").find('input[type=hidden][name="action"]').val()
|
||||
|
@ -482,16 +482,16 @@ class SubmitTests(TestCase):
|
|||
# status page as unpriviliged => no edit button
|
||||
r = self.client.get(unprivileged_status_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(("status of submission of %s" % name) in r.content.lower())
|
||||
self.assertTrue(("submission status of %s" % name) in r.content.lower())
|
||||
q = PyQuery(r.content)
|
||||
adjust_button = q('input[type=submit][value*="Adjust"]')
|
||||
adjust_button = q('[type=submit]:contains("Adjust")')
|
||||
self.assertEqual(len(adjust_button), 0)
|
||||
|
||||
# as Secretariat, we should get edit button
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
r = self.client.get(unprivileged_status_url)
|
||||
q = PyQuery(r.content)
|
||||
adjust_button = q('input[type=submit][value*="Adjust"]')
|
||||
adjust_button = q('[type=submit]:contains("Adjust")')
|
||||
self.assertEqual(len(adjust_button), 1)
|
||||
|
||||
action = adjust_button.parents("form").find('input[type=hidden][name="action"]').val()
|
||||
|
@ -521,13 +521,13 @@ class SubmitTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
request_button = q('input[type=submit][value*="Request full access"]')
|
||||
request_button = q('[type=submit]:contains("Request full access")')
|
||||
self.assertEqual(len(request_button), 1)
|
||||
|
||||
# request URL to be sent
|
||||
mailbox_before = len(outbox)
|
||||
|
||||
action = request_button.parents("form").find("input[type=hidden][name=\"action\"]").val()
|
||||
action = request_button.parents("form").find('input[type=hidden][name="action"]').val()
|
||||
r = self.client.post(url, dict(action=action))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
@ -650,12 +650,13 @@ class ApprovalsTestCase(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('input[type=submit]')), 1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Save")')), 1)
|
||||
|
||||
# faulty post
|
||||
r = self.client.post(url, dict(name="draft-test-nonexistingwg-something"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("errorlist" in r.content)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q("form .has-error")) > 0)
|
||||
|
||||
# add
|
||||
name = "draft-ietf-mars-foo"
|
||||
|
@ -676,7 +677,7 @@ class ApprovalsTestCase(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('input[type=submit]')), 1)
|
||||
self.assertEqual(len(q('[type=submit]:contains("Cancel")')), 1)
|
||||
|
||||
# cancel
|
||||
r = self.client.post(url, dict(action="cancel"))
|
||||
|
|
|
@ -39,7 +39,7 @@ def validate_submission(submission):
|
|||
for ext in submission.file_types.split(','):
|
||||
source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
|
||||
if not os.path.exists(source):
|
||||
errors['files'] = '"%s" was not found in the staging area.<br />We recommend you that you cancel this submission and upload your files again.' % os.path.basename(source)
|
||||
errors['files'] = '"%s" was not found in the staging area. We recommend you that you cancel this submission and upload your files again.' % os.path.basename(source)
|
||||
break
|
||||
|
||||
if not submission.title:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{# Copyright The IETF Trust 2012, All Rights Reserved #}
|
||||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
{% block title %}401 Unauthorized{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<img src="/images/ietflogo2e.png" height="80" width="140"/>
|
||||
<img class="ietflogo" src="/images/ietflogo.png" alt="IETF" style="width: 10em">
|
||||
|
||||
<h2>Authentication Required</h2>
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{# Copyright The IETF Trust 2007, All Rights Reserved #}
|
||||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
{% block title %}404 Not Found{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<img src="/images/ietflogo2e.png" height="80" width="140"/>
|
||||
<img class="ietflogo" src="/images/ietflogo.png" alt="IETF" style="width: 10em">
|
||||
|
||||
<h2>The page you were looking for couldn't be found.</h2>
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{# Copyright The IETF Trust 2007, All Rights Reserved #}
|
||||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
{% block title %}500 Internal Server Error{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<img src="/images/ietflogo2e.png" height="80" width="140">
|
||||
<img class="ietflogo" src="/images/ietflogo.png" alt="IETF" style="width: 10em">
|
||||
|
||||
<h2>Internal Server Error.</h2>
|
||||
|
||||
|
|
153
ietf/templates/base/menu.html
Normal file
153
ietf/templates/base/menu.html
Normal file
|
@ -0,0 +1,153 @@
|
|||
{% load wg_menu %}
|
||||
{% load streams_menu %}
|
||||
{% load ietf_filters community_tags %}
|
||||
|
||||
<li {% if flavor == "top" %}class="dropdown"{% else %}class="nav-header"{% endif %}>
|
||||
{% if flavor == "top" %}<a href="#" class="dropdown-toggle" data-toggle="dropdown">{% endif %}
|
||||
{#<span class="fa fa-user"></span>#}
|
||||
{% if user.is_authenticated %} {{ user }} {% else %} User {% endif %}
|
||||
{% if flavor == "top" %}
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% endif %}
|
||||
{% if request.get_full_path == "/accounts/logout/" %}
|
||||
<li><a rel="nofollow" href="/accounts/login{% if "/accounts/logout/" not in request.get_full_path %}/?next={{request.get_full_path|urlencode}}{% endif %}">Sign in</a></li>
|
||||
{% else %}
|
||||
{% if user.is_authenticated %}
|
||||
<li><a rel="nofollow" href="/accounts/logout/" >Sign out</a></li>
|
||||
<li><a rel="nofollow" href="/accounts/profile/">Edit profile</a></li>
|
||||
{% else %}
|
||||
<li><a rel="nofollow" href="/accounts/login/?next={{request.get_full_path|urlencode}}">Sign in</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<li><a href="{% url "account_index" %}">{% if request.user.is_authenticated %}Manage account{% else %}New account{% endif %}</a></li>
|
||||
<li><a href="{%url "ietf.cookies.views.settings" %}" rel="nofollow">Settings</a></li>
|
||||
|
||||
{% if user|has_role:"Area Director" %}
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>AD dashboard</li>
|
||||
<li><a href="{% url "docs_for_ad" name=user.person.full_name_as_key %}">My docs</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 "ietf.iesg.views.milestones_needing_review" %}">Milestone review</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% if user|has_role:"Secretariat" %}
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>IETF secretariat</li>
|
||||
<li><a href="/admin/iesg/telechatdate/">Telechat dates</a></li>
|
||||
<li><a href="/admin/iesg/telechatagendaitem/">Management items</a></li>
|
||||
<li><a href="{% url "ietf.iesg.views.milestones_needing_review" %}">Milestones</a></li>
|
||||
<li><a href="{% url "ietf.sync.views.discrepancies" %}">Sync discrepancies</a>
|
||||
{% endif %}
|
||||
|
||||
{% if user|has_role:"IANA" %}
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>IANA</li>
|
||||
<li><a href="{% url "ietf.sync.views.discrepancies" %}">Sync discrepancies</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% if user|has_role:"RFC Editor" %}
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>RFC Editor</li>
|
||||
<li><a href="{% url "ietf.sync.views.discrepancies" %}">Sync discrepancies</a></li>
|
||||
{% endif %}
|
||||
{% if flavor == "top" %}</ul>{% endif %}
|
||||
|
||||
<li {% if flavor == "top" %}class="dropdown"{% else %}class="nav-header"{% endif %}>
|
||||
{% if flavor == "top" %}<a href="#" class="dropdown-toggle" data-toggle="dropdown">{% endif %}
|
||||
{#<span class="fa fa-users"></span>#}
|
||||
Groups
|
||||
{% if flavor == "top" %}
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% endif %}
|
||||
|
||||
<li><a href="{% url "ietf.group.info.active_groups" group_type="wg" %}">Active WGs</a></li>
|
||||
<li><a href="{% url "ietf.group.info.active_groups" group_type="rg" %}">Active RGs</a></li>
|
||||
|
||||
{% if flavor == "top" %}<li class="divider visible-lg-block"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header visible-lg-block"{% else %}class="nav-header hidden-nojs"{% endif %}>By area/parent</li>
|
||||
{% wg_menu "facelift" %}
|
||||
<li class="hidden-lg hidden-nojs"><a href="#" data-toggle="modal" data-target="#navmodal">Jump to group</a></li>
|
||||
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>New work</li>
|
||||
<li><a href="{% url "ietf.group.info.chartering_groups" %}">Chartering WGs</a></li>
|
||||
<li><a href="{% url "ietf.group.info.bofs" group_type="wg" %}">BOFs</a></li>
|
||||
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>Other groups</li>
|
||||
<li><a href="{% url "ietf.group.info.concluded_groups" %}">Concluded WGs</a></li>
|
||||
<li><a href="//www.ietf.org/list/nonwg.html">Non-WG lists</a></li>
|
||||
|
||||
{% if flavor == "top" %}</ul>{% endif %}
|
||||
|
||||
<li {% if flavor == "top" %}class="dropdown"{% else %}class="nav-header"{% endif %}>
|
||||
{% if flavor == "top" %}<a href="#" class="dropdown-toggle" data-toggle="dropdown">{% endif %}
|
||||
{#<span class="fa fa-file-text"></span>#}
|
||||
Documents
|
||||
{% if flavor == "top" %}
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% endif %}
|
||||
<li><a href="{% url "submit_upload_submission" %}">Submit a draft</a></li>
|
||||
|
||||
{% if user|has_role:"WG Chair" %}
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>WG chair</li>
|
||||
<li><a href="{% url "submit_approvals" %}">Approve a draft</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% get_user_managed_lists user as community_lists %}
|
||||
{% if community_lists %}
|
||||
<li><a href="{{ community_lists.personal.get_manage_url }}">My tracked docs</a></li>
|
||||
{% for cl in community_lists.group %}
|
||||
<li><a href="{{ cl.get_manage_url }}">WG {{ cl.short_name }} docs</a></li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li><a rel="nofollow" href="/accounts/login/?next={{request.get_full_path|urlencode}}">Sign in to track docs</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% if user|has_role:"Area Director,Secretariat" %}
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li><a href="{% url "rfc_status_changes" %}">RFC status changes</a></li>
|
||||
{% endif %}
|
||||
{% if flavor == "top" %}<li class="divider hidden-xs"></li>{% endif %}
|
||||
<li {%if flavor == "top" %}class="dropdown-header hidden-xs"{% else %}class="nav-header"{% endif %}>RFC streams</li>
|
||||
<li><a href="{% url "ietf.group.views_stream.streams" %}iab/">IAB</a></li>
|
||||
<li><a href="{% url "ietf.group.views_stream.streams" %}irtf/">IRTF</a></li>
|
||||
<li><a href="{% url "ietf.group.views_stream.streams" %}ise/">ISE</a></li>
|
||||
|
||||
{% if flavor == "top" %}</ul>{% endif %}
|
||||
|
||||
<li {% if flavor == "top" %}class="dropdown"{% else %}class="nav-header"{% endif %}>
|
||||
{% if flavor == "top" %}<a href="#" class="dropdown-toggle" data-toggle="dropdown">{% endif %}
|
||||
{#<span class="fa fa-comments"></span>#}
|
||||
Meetings
|
||||
{% if flavor == "top" %}
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% endif %}
|
||||
|
||||
<li><a href="/meeting/agenda/">Agenda</a></li>
|
||||
<li><a href="/meeting/">Materials</a></li>
|
||||
<li><a href="//www.ietf.org/meeting/proceedings.html">Past proceedings</a></li>
|
||||
<li><a href="//www.ietf.org/meeting/upcoming.html">Upcoming</a></li>
|
||||
<li><a href="/meeting/requests">Session requests</a></li>
|
||||
{% if flavor == "top" %}</ul>{% endif %}
|
||||
|
||||
<li {% if flavor == "top" %}class="dropdown"{% else %}class="nav-header"{% endif %}>
|
||||
{% if flavor == "top" %}<a href="#" class="dropdown-toggle" data-toggle="dropdown">{% endif %}
|
||||
{#<span class="fa fa-folder"></span>#}
|
||||
Other
|
||||
{% if flavor == "top" %}
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% endif %}
|
||||
|
||||
<li><a href="/ipr/">IPR disclosures</a></li>
|
||||
<li><a href="/liaison/">Liaison statements</a></li>
|
||||
<li><a href="/iesg/agenda/">IESG agenda</a></li>
|
||||
{% if flavor == "top" %}</ul>{% endif %}
|
19
ietf/templates/base/menu_wg.html
Normal file
19
ietf/templates/base/menu_wg.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% for area in areas %}
|
||||
<li class="hidden-nojs dropdown-submenu visible-lg-block">
|
||||
<a href="/wg/#{{ area.acronym }}">{{ area.short_area_name }}</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% for g in area.active_groups %}
|
||||
<li><a href="{% url "ietf.group.info.group_home" group_type=g.type_id acronym=g.acronym %}">{{ g.acronym }} — {{ g.name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
<li class="hidden-nojs dropdown-submenu visible-lg-block">
|
||||
<a href="">IRTF</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% for group in rgs %}
|
||||
<li><a href="{% url "ietf.group.info.group_home" group_type=group.type_id acronym=group.acronym %}">{{ group.acronym }} — {{ group.name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
42
ietf/templates/base/menu_wg_modal.html
Normal file
42
ietf/templates/base/menu_wg_modal.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<div class="table-responsive">
|
||||
{% spaceless %}
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for area in areas %}
|
||||
<th class="text-center">{{area.acronym|upper}}</th>
|
||||
{% endfor %}
|
||||
<th class="text-center">IRTF</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% for area in areas %}
|
||||
<td>
|
||||
<div class="btn-group-vertical btn-block">
|
||||
{% for group in area.active_groups %}
|
||||
<div class="btn-group btn-group-xs btn-group-justified">
|
||||
<a href="{% url "ietf.group.info.group_home" group_type=group.type_id acronym=group.acronym %}" class="btn btn-default">
|
||||
{{group.acronym}}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td>
|
||||
<div class="btn-group-vertical btn-block">
|
||||
{% for group in rgs %}
|
||||
<div class="btn-group btn-group-xs btn-group-justified">
|
||||
<a href="{% url "ietf.group.info.group_home" group_type=group.type_id acronym=group.acronym %}" class="btn btn-default">
|
||||
{{group.acronym}}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endspaceless %}
|
||||
</div>
|
|
@ -1,18 +1,24 @@
|
|||
<h2>Display customization</h2>
|
||||
{% load bootstrap3 %}
|
||||
|
||||
<form action="#custom" method="post" />{% csrf_token %}
|
||||
<h3>Sort method</h2>
|
||||
{{ display_form.sort_method }}
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<h3>Show fields</h2>
|
||||
<div>
|
||||
{% for field in dc.get_display_fields_config %}
|
||||
<div style="float: left; width: 30%;">
|
||||
<input id="id_{{ field.codename }}" type="checkbox" name="{{ field.codename }}"{% if field.active %} checked="checked"{% endif %} />
|
||||
<label for="id_{{ field.codename }}">{{ field.description }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div style="clear: both;"><br /></div>
|
||||
<input type="submit" value="Save configuration" name="save_display" />
|
||||
<form role="form" method="post" action="#custom">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form display_form %}
|
||||
|
||||
<div class="form-group">
|
||||
<label>Show fields</label>
|
||||
{% for field in dc.get_display_fields_config %}
|
||||
<div class="checkbox">
|
||||
<label for="id_{{ field.codename }}">
|
||||
<input id="id_{{ field.codename }}" type="checkbox" name="{{ field.codename }}" {% if field.active %}checked{% endif %}>
|
||||
{{ field.description }}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% buttons %}
|
||||
<input type="submit" class="btn btn-primary" name="save_display">
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
|
|
@ -1,124 +1,130 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}{{ cl.long_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ cl.long_name }}</h1>
|
||||
<div id="mytabs" class="yui-navset">
|
||||
<ul class="yui-nav">
|
||||
<li class="selected"><a href="#view"><em>Documents</em></a></li>
|
||||
<li><a href="#documents"><em>Explicitly added</em></a></li>
|
||||
<li><a href="#rules"><em>Rules</em></a></li>
|
||||
<li><a href="#custom"><em>Display customization</em></a></li>
|
||||
<li><a href="#info"><em>Exports</em></a></li>
|
||||
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<ul class="nav nav-tabs nav-memory">
|
||||
<li class="active"><a href="#view" data-toggle="tab">Documents</a></li>
|
||||
<li><a href="#documents" data-toggle="tab">Explicitly added</a></li>
|
||||
<li><a href="#rules" data-toggle="tab">Rules</a></li>
|
||||
<li><a href="#custom" data-toggle="tab">Display customization</a></li>
|
||||
<li><a href="#info" data-toggle="tab">Exports</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="yui-content">
|
||||
<p></p>
|
||||
|
||||
<div id="view">
|
||||
{% include "community/view_list.html" %}
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="view">
|
||||
{% include "community/view_list.html" %}
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="documents">
|
||||
<p>
|
||||
In order to add some individual documents to your list, you have to:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Search for the document or documents you want to add using the datatracker search form.</li>
|
||||
<li>In the search results, you'll find a link to add individual documents to your list.</li>
|
||||
</ul>
|
||||
<a class="btn btn-default" href="/doc/search">Search documents</a>
|
||||
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th><th>State</th><th>Title</th><th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for doc in cl.added_ids.all %}
|
||||
<tr>
|
||||
<td>{{ doc.display_name }}</td>
|
||||
<td>{{ doc.get_state }}</td>
|
||||
<td><a href="{{ doc.get_absolute_url }}">{{ doc.title }}</a></td>
|
||||
<td><a class="btn btn-danger btn-xs" href="{% url "community_remove_document" cl.pk doc.pk %}">Remove</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="rules">
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr><th>Rule</th><th>Value</th><th>Documents</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for rule in cl.rule_set.all %}
|
||||
{% with rule.get_callable_rule as callable %}
|
||||
<tr>
|
||||
<td>{{ callable.description }}</td>
|
||||
<td>{{ callable.show_value }}</td>
|
||||
<td>{% with rule.cached_ids.count as count %}{{ count }} document{{ count|pluralize }}{% endwith %}</td>
|
||||
<td><a class="btn btn-danger btn-xs" href="{% url "community_remove_rule" cl.pk rule.pk %}">Remove</a></td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Add a new rule</h3>
|
||||
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form rule_form %}
|
||||
|
||||
{% buttons %}
|
||||
<input type="submit" class="btn btn-primary" name="save_rule" value="Add rule">
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="custom">
|
||||
{% include "community/customize_display.html" %}
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="info">
|
||||
<p>Feel free to share the following links if you need to:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="{{ cl.secret }}/view/">Read only view for {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/changes/feed/">Feed for every change in status of {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/changes/significant/feed/">Feed for significant change in status of {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/subscribe/">Subscribe to the mailing list for every change in status of {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/subscribe/significant/">Subscribe to the mailing list for significant change in status of {{ cl.long_name }}</a></li>
|
||||
</ul>
|
||||
|
||||
<p>Export your list to CSV format:</p>
|
||||
<ul>
|
||||
<li><a href="csv/">CSV for {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/csv/">Read only CSV for {{ cl.long_name }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="documents">
|
||||
<h2>Documents explicitly included, from a document search.</a></h2>
|
||||
<p>
|
||||
In order to add some individual documents to your list you have to:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Search for the document or documents you want to add using the datatracker search form.</li>
|
||||
<li>In the search result you'll find a link to add individual documents to your list.</li>
|
||||
</ul>
|
||||
<p>
|
||||
<a href="/doc/search"><b>Go to the Search Form to search for and add specific documents</b></a>
|
||||
</p>
|
||||
|
||||
<table class="ietf-table">
|
||||
<tr><th>Name</th><th>State</th><th>Title</th><th>Remove from list</th></tr>
|
||||
{% for doc in cl.added_ids.all %}
|
||||
<tr class="{% cycle oddrow,evenrow %}">
|
||||
<td>{{ doc.display_name }}</td>
|
||||
<td>{{ doc.get_state }}</td>
|
||||
<td><a href="{{ doc.get_absolute_url }}"</a>{{ doc.title }}</td>
|
||||
<td><a href="{% url "community_remove_document" cl.pk doc.pk %}">Remove</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="rules">
|
||||
<h2>Rules added to this list</h2>
|
||||
<table class="ietf-table">
|
||||
<tr><th>Rule</th><th>Value</th><th>Documents</th><th>Remove from list</th></tr>
|
||||
{% for rule in cl.rule_set.all %}
|
||||
{% with rule.get_callable_rule as callable %}
|
||||
<tr class="{% cycle oddrow,evenrow %}">
|
||||
<td>{{ callable.description }}</td>
|
||||
<td>{{ callable.show_value }}</td>
|
||||
<td>{% with rule.cached_ids.count as count %}{{ count }} document{{ count|pluralize }}{% endwith %}</td>
|
||||
<td><a href="{% url "community_remove_rule" cl.pk rule.pk %}">Remove</a></td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<h3>Add a new rule</h3>
|
||||
<form method="post" action="#rules">{% csrf_token %}
|
||||
{{ rule_form.as_p }}
|
||||
<input type="submit" name="save_rule" value="Add rule" />
|
||||
</form>
|
||||
<div class="rule_values" style="display: none;">
|
||||
{% for entry in rule_form.get_all_options %}
|
||||
<div class="{{ entry.type }}">
|
||||
<select>
|
||||
{% for option in entry.options %}
|
||||
<option value={{ option.0 }}>{{ option.1|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="custom">
|
||||
{% include "community/customize_display.html" %}
|
||||
</div>
|
||||
|
||||
<div id="info">
|
||||
<p>Feel free to share the following links if you need it.</p>
|
||||
<ul>
|
||||
<li><a href="{{ cl.secret }}/view/">Read only view for {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/changes/feed/">Feed for every change in status of {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/changes/significant/feed/">Feed for significant change in status of {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/subscribe/">Subscribe to the mailing list for every change in status of {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/subscribe/significant/">Subscribe to the mailing list for significant change in status of {{ cl.long_name }}</a></li>
|
||||
</ul>
|
||||
|
||||
<p>Export your list to CSV format</p>
|
||||
<ul>
|
||||
<li><a href="csv/">CSV for {{ cl.long_name }}</a></li>
|
||||
<li><a href="{{ cl.secret }}/csv/">Read only CSV for {{ cl.long_name }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript" src="/js/community.js"></script>
|
||||
{% endblock js %}
|
||||
{% comment %}
|
||||
XXX scrolling jumps around when using this, unfortunately
|
||||
<script>
|
||||
$('.nav-memory a').click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).tab('show');
|
||||
});
|
||||
|
||||
{% block scripts %}
|
||||
var tabView = new YAHOO.widget.TabView('mytabs');
|
||||
var url = location.href.split('#');
|
||||
if (url[1]) {
|
||||
url[1] = "#"+url[1];
|
||||
var tabs = tabView.get('tabs');
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (url[1].indexOf(tabs[i].get('href')) == 0) {
|
||||
tabView.set('activeIndex', i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
{% endblock %}
|
||||
// store the currently selected tab in the hash value
|
||||
$("ul.nav-tabs > li > a").on("shown.bs.tab", function (e) {
|
||||
var id = $(e.target).attr("href").substr(1);
|
||||
window.location.hash = id;
|
||||
});
|
||||
|
||||
// on load of the page: switch to the currently selected tab
|
||||
var hash = window.location.hash;
|
||||
$('.nav-memory a[href="' + hash + '"]').tab('show');
|
||||
</script>
|
||||
{% endcomment %}
|
||||
|
|
|
@ -1,22 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Subscribe to {{ cl.long_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Subscribe to {{ cl.long_name }}</h1>
|
||||
|
||||
{% if success %}
|
||||
<p>
|
||||
We have sent an email to your email address with instructions to complete your subscription.
|
||||
</p>
|
||||
<h1>Subscription successful</h1>
|
||||
|
||||
<p>We have sent an email to your email address with instructions to complete your subscription.</p>
|
||||
{% else %}
|
||||
<p>
|
||||
Subscribe to the email list for notifications of {% if significant %}significant {% endif %}changes on {{ cl.long_name }}.
|
||||
</p>
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{{ form }}
|
||||
</table>
|
||||
<input type="submit" value="Subscribe" />
|
||||
</form>
|
||||
<h1>Subscribe to {{ cl.long_name }}</h1>
|
||||
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<p>Subscribe to the email list for notifications of {% if significant %}significant {% endif %}changes on {{ cl.long_name }}.</p>
|
||||
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Subscribe</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Subscription to {{ cl.long_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Subscription to {{ cl.long_name }}</h1>
|
||||
|
||||
<p>Your email address {{ email }} has been successfully subscribed to {{ cl.long_name }}.</p>
|
||||
|
||||
<p>
|
||||
You email address {{ email }} has been successfully subscribed to {{ cl.long_name }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{% url "view_personal_list" secret=cl.secret %}">Return to the list view</a>
|
||||
<a class="btn btn-primary" href="{% url "view_personal_list" secret=cl.secret %}">Back</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Cancel subscription to {{ cl.long_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Cancel subscription to {{ cl.long_name }}</h1>
|
||||
|
||||
{% if success %}
|
||||
<p>
|
||||
You will receive a confirmation email shortly containing further instructions on how to cancel your subscription.
|
||||
</p>
|
||||
<h1>Cancellation successful</h1>
|
||||
|
||||
<p>
|
||||
You will receive a confirmation email shortly containing further instructions on how to cancel your subscription.
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
Cancel your subscription to the email list for notifications of {% if significant %}significant {% endif %}changes on {{ cl.long_name }}.
|
||||
</p>
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{{ form }}
|
||||
</table>
|
||||
<input type="submit" value="Cancel subscription" />
|
||||
</form>
|
||||
<h1>Cancel subscription to {{ cl.long_name }}</h1>
|
||||
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<p>
|
||||
Cancel your subscription to the email list for notifications of {% if significant %}significant {% endif %}changes on {{ cl.long_name }}.
|
||||
</p>
|
||||
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Subscribe</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Canceled subscription to {{ cl.long_name }}{% endblock %}
|
||||
{% block title %}Cancelled subscription to {{ cl.long_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Canceled subscription to {{ cl.long_name }}</h1>
|
||||
<h1>Cancelled subscription to {{ cl.long_name }}</h1>
|
||||
|
||||
<p>
|
||||
You email address {{ email }} has been successfully removed form {{ cl.long_name }} {% if significant %}significant {% endif %}changes mailing list.
|
||||
Your email address {{ email }} has been successfully removed from the {{ cl.long_name }} {% if significant %}significant {% endif %}changes mailing list.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="{{ cl.get_public_url }}">Return to the list view</a>
|
||||
<a class="btn btn-primary" href="{{ cl.get_public_url }}">Back</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="alternate" type="application/rss+xml" title="Changes on {{ cl.long_name }} RSS Feed" href="../changes/feed/" />
|
||||
<link rel="alternate" type="application/rss+xml" title="Significant changes on {{ cl.long_name }} RSS Feed" href="../changes/significant/feed/" />
|
||||
<link rel="alternate" type="application/atom+xml" title="Changes on {{ cl.long_name }}" href="../changes/feed/" />
|
||||
<link rel="alternate" type="application/atom+xml" title="Significant changes on {{ cl.long_name }}" href="../changes/significant/feed/" />
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ cl.long_name }}{% endblock %}
|
||||
|
@ -10,7 +10,7 @@
|
|||
{% block content %}
|
||||
<h1>{{ cl.long_name }}</h1>
|
||||
<p>
|
||||
Subscribe to notification email list:
|
||||
Subscribe to notification email lists:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="../subscribe/">All changes email list</a></li>
|
||||
|
|
|
@ -3,42 +3,45 @@
|
|||
{% with cl.get_rfcs_and_drafts as documents %}
|
||||
{% with dc.get_active_fields as fields %}
|
||||
<h2>Drafts</h2>
|
||||
<table class="ietf-table">
|
||||
<tr>
|
||||
{% for field in fields %}
|
||||
<th>{{ field.description }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% for doc in documents.1 %}
|
||||
<tr class="{% cycle oddrow,evenrow %}">
|
||||
{% for field in fields %}
|
||||
<td>
|
||||
{% show_field field doc %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for field in fields %}
|
||||
<th>{{ field.description }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for doc in documents.1 %}
|
||||
<tr>
|
||||
{% for field in fields %}
|
||||
<td>{% show_field field doc %}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endwith %}
|
||||
|
||||
<h2>RFCs</h2>
|
||||
{% with dc.get_active_fields as fields %}
|
||||
<table class="ietf-table">
|
||||
<tr>
|
||||
{% for field in fields %}
|
||||
<th>{{ field.rfcDescription }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% for doc in documents.0 %}
|
||||
<tr class="{% cycle oddrow,evenrow %}">
|
||||
{% for field in fields %}
|
||||
<td>
|
||||
{% show_field field doc %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<h2>RFCs</h2>
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for field in fields %}
|
||||
<th>{{ field.rfcDescription }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for doc in documents.0 %}
|
||||
<tr>
|
||||
{% for field in fields %}
|
||||
<td>{% show_field field doc %}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endwith %}
|
||||
|
||||
{% endwith %}
|
||||
|
|
|
@ -1,68 +1,51 @@
|
|||
{# Copyright The IETF Trust 2010, All Rights Reserved #}
|
||||
{% extends "base.html" %}
|
||||
{% load ietf_filters %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}User settings{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>Cookie settings for the IETF datatracker</h2>
|
||||
<h1>User settings</h1>
|
||||
|
||||
<p> Following settings are implemented using cookies, so if you have
|
||||
cookies disabled then you are not able to change the settings
|
||||
(everything still continues to work by using default settings).</p>
|
||||
<p>
|
||||
The following settings are implemented using cookies, so if you have
|
||||
cookies disabled then you will not be able to change the settings
|
||||
(everything still continues to work by using default settings).
|
||||
</p>
|
||||
|
||||
<table id="settings">
|
||||
<tr class="setting-header">
|
||||
<td colspan="6">
|
||||
<h2 class="ietf-divider">How many days is considered new</h2>
|
||||
</td>
|
||||
<tr>
|
||||
<tr class="setting-description">
|
||||
<td colspan="6">
|
||||
<p>This setting affects how many days is considered new enough to get the special marking in the drafts page. Default setting is 14 days.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="settings-values">
|
||||
<td>{% if new_enough == 7 %}<span class="ietf-highlight-y"><a href="/accounts/settings/new_enough/7">7 days</a></span>{%else%}<a href="/accounts/settings/new_enough/7">7 days</a>{% endif %}</td></td>
|
||||
<td>{% if new_enough == 14 %}<span class="ietf-highlight-y"><a href="/accounts/settings/new_enough/14">14 days</a></span>{%else%}<a href="/accounts/settings/new_enough/14">14 days</a>{% endif %}</td>
|
||||
<td>{% if new_enough == 21 %}<span class="ietf-highlight-y"><a href="/accounts/settings/new_enough/21">21 days</a></span>{%else%}<a href="/accounts/settings/new_enough/21">21 days</a>{% endif %}</td>
|
||||
<td>{% if new_enough == 30 %}<span class="ietf-highlight-y"><a href="/accounts/settings/new_enough/30">30 days</a></span>{%else%}<a href="/accounts/settings/new_enough/30">30 days</a>{% endif %}</td>
|
||||
<td>{% if new_enough == 60 %}<span class="ietf-highlight-y"><a href="/accounts/settings/new_enough/60">60 days</a></span>{%else%}<a href="/accounts/settings/new_enough/60">60 days</a>{% endif %}</td>
|
||||
<td>{% if new_enough == 90 %}<span class="ietf-highlight-y"><a href="/accounts/settings/new_enough/90">90 days</a></span>{%else%}<a href="/accounts/settings/new_enough/90">90 days</a>{% endif %}</td>
|
||||
</tr>
|
||||
<h2>How many days is considered "new"?</h2>
|
||||
|
||||
<tr class="setting-header">
|
||||
<td colspan="6">
|
||||
<h2 class="ietf-divider">How many days is considered soon</h2>
|
||||
</td>
|
||||
<tr>
|
||||
<tr class="setting-description">
|
||||
<td colspan="6">
|
||||
<p>This setting tells what is considered soon when showing document which is going to be expire soon. Default setting is 14 days.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="settings-values">
|
||||
<td>{% if expires_soon == 7 %}<span class="ietf-highlight-y"><a href="/accounts/settings/expires_soon/7">7 days</a></span>{%else%}<a href="/accounts/settings/expires_soon/7">7 days</a>{% endif %}</td></td>
|
||||
<td>{% if expires_soon == 14 %}<span class="ietf-highlight-y"><a href="/accounts/settings/expires_soon/14">14 days</a></span>{%else%}<a href="/accounts/settings/expires_soon/14">14 days</a>{% endif %}</td>
|
||||
<td>{% if expires_soon == 21 %}<span class="ietf-highlight-y"><a href="/accounts/settings/expires_soon/21">21 days</a></span>{%else%}<a href="/accounts/settings/expires_soon/21">21 days</a>{% endif %}</td>
|
||||
<td>{% if expires_soon == 30 %}<span class="ietf-highlight-y"><a href="/accounts/settings/expires_soon/30">30 days</a></span>{%else%}<a href="/accounts/settings/expires_soon/30">30 days</a>{% endif %}</td>
|
||||
<td>{% if expires_soon == 60 %}<span class="ietf-highlight-y"><a href="/accounts/settings/expires_soon/60">60 days</a></span>{%else%}<a href="/accounts/settings/expires_soon/60">60 days</a>{% endif %}</td>
|
||||
<td>{% if expires_soon == 90 %}<span class="ietf-highlight-y"><a href="/accounts/settings/expires_soon/90">90 days</a></span>{%else%}<a href="/accounts/settings/expires_soon/90">90 days</a>{% endif %}</td>
|
||||
</tr>
|
||||
<p>This setting affects how many days are considered "new enough" to get the special highlighting in the documents table. Default setting is 14 days.</p>
|
||||
|
||||
<div class="btn-group btn-group-justified">
|
||||
<a class="btn btn-default {% if new_enough == 7 %}active{% endif %}" href="/accounts/settings/new_enough/7">7 days</a>
|
||||
<a class="btn btn-default {% if new_enough == 14 %}active{% endif %}" href="/accounts/settings/new_enough/14">14 days</a>
|
||||
<a class="btn btn-default {% if new_enough == 21 %}active{% endif %}" href="/accounts/settings/new_enough/21">21 days</a>
|
||||
<a class="btn btn-default {% if new_enough == 30 %}active{% endif %}" href="/accounts/settings/new_enough/30">30 days</a>
|
||||
<a class="btn btn-default {% if new_enough == 60 %}active{% endif %}" href="/accounts/settings/new_enough/60">60 days</a>
|
||||
<a class="btn btn-default {% if new_enough == 90 %}active{% endif %}" href="/accounts/settings/new_enough/90">90 days</a>
|
||||
</div>
|
||||
|
||||
<h2 class="ietf-divider">How many days is considered "soon"?</h2>
|
||||
|
||||
<p>This setting tells what is considered "soon" when showing documents that are going to be expire soon. Default setting is 14 days.</p>
|
||||
|
||||
<div class="btn-group btn-group-justified">
|
||||
<a class="btn btn-default {% if expires_soon == 7 %}active{% endif %}" href="/accounts/settings/expires_soon/7">7 days</a>
|
||||
<a class="btn btn-default {% if expires_soon == 14 %}active{% endif %}" href="/accounts/settings/expires_soon/14">14 days</a>
|
||||
<a class="btn btn-default {% if expires_soon == 21 %}active{% endif %}" href="/accounts/settings/expires_soon/21">21 days</a>
|
||||
<a class="btn btn-default {% if expires_soon == 30 %}active{% endif %}" href="/accounts/settings/expires_soon/30">30 days</a>
|
||||
<a class="btn btn-default {% if expires_soon == 60 %}active{% endif %}" href="/accounts/settings/expires_soon/60">60 days</a>
|
||||
<a class="btn btn-default {% if expires_soon == 90 %}active{% endif %}" href="/accounts/settings/expires_soon/90">90 days</a>
|
||||
</div>
|
||||
|
||||
<h2 class="ietf-divider">Show full document text by default?</h2>
|
||||
|
||||
<p>Show the full text immediately on the document page instead of only showing beginning of it. This defaults to off.</p>
|
||||
|
||||
<div class="btn-group btn-group-justified">
|
||||
<a class="btn btn-default {% if full_draft == "off" %}active{% endif %}" href="/accounts/settings/full_draft/off">Off</a>
|
||||
<a class="btn btn-default {% if full_draft == "on" %}active{% endif %}" href="/accounts/settings/full_draft/on">On</a>
|
||||
</div>
|
||||
|
||||
<tr class="setting-header">
|
||||
<td colspan="6">
|
||||
<h2 class="ietf-divider">Show full document text in document page</h2>
|
||||
</td>
|
||||
<tr>
|
||||
<tr class="setting-description">
|
||||
<td colspan="6">
|
||||
<p>Show the full draft immediately on the document page instead of only showing beginning of it. This defaults to off.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="settings-values">
|
||||
<td>{% if full_draft == "off" %}<span class="ietf-highlight-y"><a href="/accounts/settings/full_draft/off">off</a></span>{%else%}<a href="/accounts/settings/full_draft/off">off</a>{% endif %}</td></td>
|
||||
<td>{% if full_draft == "on" %}<span class="ietf-highlight-y"><a href="/accounts/settings/full_draft/on">on</a></span>{%else%}<a href="/accounts/settings/full_draft/on">on</a>{% endif %}</td></td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -1,33 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Add comment on {{ doc }}{% endblock %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block morecss %}
|
||||
form.add-comment #id_comment {
|
||||
width: 600px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
form.add-comment .actions {
|
||||
padding-top: 20px;
|
||||
}
|
||||
{% endblock %}
|
||||
{% block title %}Add comment for {{ doc }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Add comment on {{ doc }}</h1>
|
||||
<h1>Add comment<br><small>{{ doc }}</small></h1>
|
||||
|
||||
<p>The comment will be added to the history trail.</p>
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<form class="add-comment" action="" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url "doc_history" name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Add comment"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
<p class="help-block">The comment will be added to the history trail.</p>
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,36 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block title %}Approval announcement writeup for {{ doc }}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form #id_approval_text {
|
||||
width: 700px;
|
||||
height: 600px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Approval announcement writeup for {{ doc }}</h1>
|
||||
<h1>Approval announcement writeup<br><small>{{ doc }}</small></h1>
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<p>Sent after approval.</p>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form approval_text_form %}
|
||||
|
||||
{{ approval_text_form.approval_text }}
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ back_url }}">Back</a>
|
||||
<input type="submit" name="save_approval_text" value="Save Approval Announcement Text" />
|
||||
<input type="submit" name="regenerate_approval_text" value="Regenerate Approval Announcement Text" />
|
||||
</div>
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary" name="save_approval_text">Save text</button>
|
||||
<button type="submit" class="btn btn-warning" name="regenerate_approval_text">Regenerate text</button>
|
||||
{% if user|has_role:"Secretariat" and can_announce %}
|
||||
<a class="btn btn-default" href="{% url "doc_approve_ballot" name=doc.name %}">Approve ballot</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% load ietf_filters %}
|
||||
{% if user|has_role:"Secretariat" %}
|
||||
<p>
|
||||
{% if can_announce %}
|
||||
<a href="{% url "doc_approve_ballot" name=doc.name %}">Approve ballot</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock%}
|
||||
|
|
|
@ -1,45 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Approve ballot for {{ doc }}{% 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 {{ doc }}</h1>
|
||||
<h1>Approve ballot<br><small>{{ doc }}</small></h1>
|
||||
|
||||
<div>IETF announcement:</div>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
<pre>{{ announcement }}</pre>
|
||||
|
||||
<form class="approve-ballot" action="" method="post">{% csrf_token %}
|
||||
|
||||
<div class="announcement">
|
||||
<pre>{{ announcement }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<a href="{% url "doc_ballot_approvaltext" name=doc.name %}">Back</a>
|
||||
{% if action == "to_announcement_list" %}
|
||||
<input type="submit" value="Notify the RFC Editor, send out the announcement and close ballot"/>
|
||||
{% endif %}
|
||||
{% if action == "to_rfc_editor" %}
|
||||
<input type="submit" value="Send message to the RFC Editor and close ballot"/>
|
||||
{% endif %}
|
||||
{% if action == "do_not_publish" %}
|
||||
<input type="submit" value="Send message to the RFC Editor (DNP) and close ballot"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="buttonlist">
|
||||
{% if action == "to_announcement_list" %}
|
||||
<button class="btn btn-warning" type="submit">Notify RFC Editor, send announcement & close ballot</button>
|
||||
{% elif action == "to_rfc_editor" %}
|
||||
<button class="btn btn-warning" type="submit">Email RFC Editor & close ballot</button>
|
||||
{% elif action == "do_not_publish" %}
|
||||
<button class="btn btn-warning" type="submit">Email RFC Editor (DNP) & close ballot"/>
|
||||
{% endif %}
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_ballot_approvaltext" name=doc.name %}">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Ballot for {{ doc }} issued{% endblock %}
|
||||
{% block title %}Ballot issued for {{ doc }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Ballot for {{ doc }} issued</h1>
|
||||
<h1>Ballot issued<br><small>{{ doc }}</small></h1>
|
||||
|
||||
<p>Ballot has been sent out.</p>
|
||||
|
||||
{% if doc.telechat_date %}
|
||||
<p>The document is currently on the {{ doc.telechat_date }} telechat agenda.</p>
|
||||
<p>The document is currently on the {{ doc.telechat_date }} telechat agenda.</p>
|
||||
{% else %}
|
||||
<p>The document is not on any telechat agenda.</p>
|
||||
<p>The document is not on any telechat agenda.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ back_url }}">Back to document</a>
|
||||
</div>
|
||||
<a class="btn btn-default pull-right" href="{{ back_url }}">Back</a>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Clear ballot for {{ doc }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Clear ballot for {{ doc }}</h1>
|
||||
<h1>Clear ballot<br><small>{{ doc }}</small></h1>
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<p>Clear the ballot for {{ doc.file_tag }}?</p>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
<p class="alert alert-danger">
|
||||
<b>Clear the ballot for {{ doc }}?</b>
|
||||
<br>
|
||||
This will clear all ballot positions and discuss entries.
|
||||
</p>
|
||||
|
||||
<p>This will clear all ballot positions and discuss entries.</p>
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ back_url }}ballot/">Back</a>
|
||||
<input type="submit" value="Clear ballot"/>
|
||||
</div>
|
||||
<div class="buttonlist">
|
||||
<button type="submit" class="btn btn-danger">Clear</button>
|
||||
<a class="btn btn-default pull-right" href="{{ back_url }}ballot/">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Defer ballot for {{ doc }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Defer ballot for {{ doc }}</h1>
|
||||
<h1>Defer ballot<br><small>{{ doc }}</small></h1>
|
||||
|
||||
<form class="defer" action="" method="post">{% csrf_token %}
|
||||
<p>Defer the ballot for {{ doc.file_tag }}?</p>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
<p class="alert alert-danger">
|
||||
<b>Defer the ballot for {{ doc }}?</b>
|
||||
<br>
|
||||
The ballot will then be put on the IESG agenda of {{ telechat_date }}.
|
||||
</p>
|
||||
|
||||
<p>The ballot will then be on the IESG agenda of {{ telechat_date }}.</p>
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ back_url }}">Back</a>
|
||||
<input type="submit" value="Defer ballot"/>
|
||||
</div>
|
||||
<div class="buttonlist">
|
||||
<input type="submit" class="btn btn-danger" value="Defer ballot">
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,92 +1,64 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Change position for {{ ad.plain_name }} on {{ doc }}{% 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 .discuss-text {
|
||||
padding-top: 20px
|
||||
}
|
||||
form.position-form #id_discuss,
|
||||
form.position-form #id_comment {
|
||||
width: 700px;
|
||||
height: 250px;
|
||||
}
|
||||
form.position-form .comment-text {
|
||||
margin-top: 20px;
|
||||
}
|
||||
div.question {
|
||||
font-size: 173%;
|
||||
padding-left: 5px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change position for {{ ad.plain_name }} on {{ doc }}</h1>
|
||||
<h1>Change position for {{ ad.plain_name }}<br><small>{{ doc }}</small></h1>
|
||||
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<div class="question">{{ ballot.ballot_type.question }}</div>
|
||||
|
||||
{% if ballot_deferred %}
|
||||
<div class="ballot-deferred">Ballot deferred by {{ ballot_deferred.by }} on {{ ballot_deferred.time|date:"Y-m-d" }}.</div>
|
||||
<p class="alert alert-info">
|
||||
Ballot deferred by {{ ballot_deferred.by }} on {{ ballot_deferred.time|date:"Y-m-d" }}.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form class="position-form" action="" method="post">{% csrf_token %}
|
||||
<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"/>
|
||||
{% if doc.type_id == "draft" or doc.type_id == "conflrev" %}
|
||||
{% if ballot_deferred %}<input type="submit" name="Undefer" value="Undefer"/>{% else %}<input type="submit" name="Defer" value="Defer"/>{% endif %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div style="clear:left"></div>
|
||||
|
||||
<div class="discuss-widgets"{% if not show_discuss_text %} style="display:none"{% endif %}>
|
||||
<div class="discuss-text">
|
||||
{{ form.discuss.label_tag }}
|
||||
{% if old_pos and old_pos.discuss_time %}<span class="last-edited">(last edited {{ old_pos.discuss_time }})</span>{% endif %}
|
||||
</div>
|
||||
{{ form.discuss.errors }}
|
||||
{{ form.discuss }}
|
||||
</div>
|
||||
|
||||
<div class="comment-text">
|
||||
{{ 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 }}
|
||||
{% for field in form %}
|
||||
{% bootstrap_field field %}
|
||||
{% if field.name == "discuss" and old_pos and old_pos.discuss_time %}
|
||||
<span class="help-block">Last edited {{ old_pos.discuss_time }}</span>
|
||||
{% elif field.name == "comment" and old_pos and old_pos.comment_time %}
|
||||
<span class="help-block">Last edited {{ old_pos.comment_time }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ return_to_url }}">Back</a>
|
||||
</div>
|
||||
|
||||
{{ form.return_to_url }}
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary" name="send_mail">Save & send email</button>
|
||||
<input type="submit" class="btn btn-default" value="Save">
|
||||
|
||||
{% if doc.type_id == "draft" or doc.type_id == "conflrev" %}
|
||||
{% if ballot_deferred %}
|
||||
<input type="submit" class="btn btn-warning" name="Undefer" value="Undefer ballot">
|
||||
{% else %}
|
||||
<input type="submit" class="btn btn-danger" name="Defer" value="Defer ballot">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<a class="btn btn-default pull-right" href="{{ doc.get_absolute_url }}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block content_end %}
|
||||
<script>
|
||||
var blockingPositions = {{ blocking_positions|safe }};
|
||||
</script>
|
||||
<script type="text/javascript" src="/js/doc-edit-position.js"></script>
|
||||
{% block scripts %}
|
||||
var block_pos = {{ blocking_positions|safe }};
|
||||
|
||||
function discuss_toggle(val) {
|
||||
if (val in block_pos) {
|
||||
$("#div_id_discuss").show();
|
||||
} else {
|
||||
$("#div_id_discuss").hide();
|
||||
}
|
||||
}
|
||||
|
||||
$("input[name=position]").click(function () {
|
||||
discuss_toggle($(this).val());
|
||||
});
|
||||
|
||||
discuss_toggle($("input[name=position]:checked").val());
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,46 +1,36 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Last Call text for {{ doc }}{% endblock %}
|
||||
{% load bootstrap3 %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block morecss %}
|
||||
form #id_last_call_text {
|
||||
width: 700px;
|
||||
height: 600px;
|
||||
}
|
||||
{% endblock %}
|
||||
{% block title %}Last call text for {{ doc }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Last Call text for {{ doc }}</h1>
|
||||
<h1>Last call text<br><small>{{ doc }}</small></h1>
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<p>{{ last_call_form.last_call_text.errors }}</p>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form last_call_form %}
|
||||
|
||||
{{ last_call_form.last_call_text }}
|
||||
{% if can_request_last_call and need_intended_status %}
|
||||
<span class="help-block">
|
||||
You need to select intended status of {{ need_intended_status }} and regenerate last call text to request last call.
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<p>{{ last_call_form.last_call_text.errors }}</p>
|
||||
|
||||
{% if can_request_last_call and need_intended_status %}
|
||||
<p>You need to select intended status of {{ need_intended_status }} and regenerate last call text to request last call.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ back_url }}">Back</a>
|
||||
<input type="submit" name="save_last_call_text" value="Save Last Call Text" />
|
||||
<input type="submit" name="regenerate_last_call_text" value="Regenerate Last Call Text" />
|
||||
{% if can_request_last_call and not need_intended_status %}
|
||||
<input style="margin-left: 8px" type="submit" name="send_last_call_request" value="Save and Request Last Call" />
|
||||
{% endif %}
|
||||
</div>
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary" name="save_last_call_text">Save text</button>
|
||||
{% if can_request_last_call and not need_intended_status %}
|
||||
<button type="submit" class="btn btn-warning" name="send_last_call_request">Save text & request last call</button>
|
||||
{% endif %}
|
||||
<button type="submit" class="btn btn-warning" name="regenerate_last_call_text">Regenerate text</button>
|
||||
{% if user|has_role:"Secretariat" and can_make_last_call %}
|
||||
<a class="btn btn-danger" href="{% url "doc_make_last_call" name=doc.name %}">Issue last call</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% load ietf_filters %}
|
||||
{% if user|has_role:"Secretariat" %}
|
||||
<p>
|
||||
{% if can_make_last_call %}
|
||||
<a href="{% url "doc_make_last_call" name=doc.name %}">Make Last Call</a>
|
||||
{% endif %}
|
||||
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock%}
|
||||
|
|
|
@ -1,47 +1,55 @@
|
|||
{% extends "base.html" %}
|
||||
{% load ietf_filters %}
|
||||
{% block title %}Send ballot position email for {{ ad }}{% endblock %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
form.send-ballot pre {
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
border-top: 4px solid #eee;
|
||||
border-bottom: 4px solid #eee;
|
||||
}
|
||||
{% endblock %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block title %}Send ballot position for {{ ad }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Send ballot position email for {{ ad }}</h1>
|
||||
<h1>Send ballot position for {{ ad }}</h1>
|
||||
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="form-group">
|
||||
<label>From</label>
|
||||
<input class="form-control" type="text" placeholder="{{ frm }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>To</label>
|
||||
<input class="form-control" type="text" placeholder="{{ to }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Cc</label>
|
||||
<input class="form-control" type="email" name="cc">
|
||||
<span class="help-block">Separate email addresses with commas.</span>
|
||||
</div>
|
||||
|
||||
{% if doc.notify %}
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="cc_state_change" value="1" checked>
|
||||
<b>Cc:</b> {{ doc.notify }}
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input class="form-control" type="text" placeholder="{{ subject }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<pre>{{ body|wrap_text }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="buttonlist">
|
||||
<input type="submit" class="btn btn-danger" value="Send">
|
||||
<a class="btn btn-default pull-right" href="{{ back_url }}">Back</a>
|
||||
</div>
|
||||
|
||||
<form class="send-ballot" action="" method="post">{% csrf_token %}
|
||||
<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.notify %}
|
||||
<label>
|
||||
<input type="checkbox" name="cc_state_change" value="1" checked="checked" />
|
||||
{{ doc.notify }}
|
||||
</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 %}
|
||||
|
|
|
@ -1,34 +1,28 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% 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">{% csrf_token %}
|
||||
|
||||
<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>
|
||||
<h1>Ballot writeup and notes<br><small>{{ doc }}</small></h1>
|
||||
|
||||
{{ ballot_writeup_form.ballot_writeup }}
|
||||
|
||||
<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" name="issue_ballot" value="Save and {% if ballot_issued %}Re-{% endif %}Issue Ballot" />
|
||||
</div>
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form ballot_writeup_form %}
|
||||
|
||||
<span class="help-block">
|
||||
Technical summary, Working Group summary, document quality, personnel, RFC Editor note, IRTF note, IESG note, IANA note. This text will be appended to all announcements and messages to the IRTF or RFC Editor.
|
||||
</span>
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary" name="save_ballot_writeup">Save</button>
|
||||
<button type="submit" class="btn btn-warning" name="issue_ballot">Save & {% if ballot_issued %}re-{% endif %}issue ballot</button>
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
|
||||
{% endblock%}
|
||||
|
|
|
@ -1,14 +1,37 @@
|
|||
{% load ietf_filters %}
|
||||
<div class="ballot-popup">
|
||||
<div class="content">
|
||||
{{ ballot_content }}
|
||||
</div>
|
||||
<div class="actions">
|
||||
|
||||
{% if request.user|has_role:"Area Director" %}
|
||||
<a href="{% url "ietf.doc.views_ballot.edit_position" name=doc.name ballot_id=ballot_id %}" class="button" style="margin-right: 1em;">Edit Position</a>
|
||||
{% endif %}
|
||||
|
||||
<a href="" class="button close">Close</a>
|
||||
</div>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
<span class="sr-only">Close</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="modal-title-{{ ballot_id }}">Ballot for {{ doc.name }}</h4>
|
||||
{% if deferred %}
|
||||
<p>Ballot deferred by {{ deferred.by }} on {{ deferred.time|date:"Y-m-d" }}.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
{{ ballot_content }}
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
{% if editable and user|has_role:"Area Director,Secretariat" %}
|
||||
{% if user|has_role:"Area Director" %}
|
||||
<a class="btn btn-default" href="{% url "ietf.doc.views_ballot.edit_position" name=doc.name ballot_id=ballot_id %}">Edit position</a>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.type_id == "draft" or doc.type_id == "conflrev" %}
|
||||
{% if deferred %}
|
||||
<a class="btn btn-default" href="{% url "doc_undefer_ballot" name=doc.name %}">Undefer ballot</a>
|
||||
{% else %}
|
||||
<a class="btn btn-warning" href="{% url "doc_defer_ballot" name=doc.name %}">Defer ballot</a>
|
||||
{% endif %}
|
||||
|
||||
{% if user|has_role:"Secretariat" %}
|
||||
<a class="btn btn-danger" href="{% url "doc_clear_ballot" name=doc.name %}">Clear ballot</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
|
|
|
@ -1,39 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
{% endblock %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}
|
||||
Change the shepherding AD for {{titletext}}
|
||||
{% endblock %}
|
||||
{% block title %}Change shepherding AD for {{titletext}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change the shepherding AD for {{titletext}}</h1>
|
||||
<h1>Change shepherding AD<br><small>{{titletext}}</small></h1>
|
||||
|
||||
<form class="edit-info" action="" enctype="multipart/form-data" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url "doc_view" name=doc.canonical_name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<form role="form" enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.canonical_name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,49 +1,35 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
{% endblock %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}
|
||||
Change the document shepherd for {{ doc.name }}-{{ doc.rev }}
|
||||
Change document shepherd for {{ doc.name }}-{{ doc.rev }}
|
||||
{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2.css">
|
||||
<link rel="stylesheet" href="/facelift/css/lib/select2-bootstrap.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change the document shepherd for {{ doc.name }}-{{ doc.rev }}</h1>
|
||||
<h1>Change document shepherd<br><small>{{ doc.name }}-{{ doc.rev }}</small></h1>
|
||||
|
||||
<p>The shepherd needs to have a Datatracker account. A new account can be
|
||||
<a href="{% url "create_account" %}">created here</a>.</p>
|
||||
|
||||
<form class="edit-info" action="" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}</th>
|
||||
<td>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a class="button" href="{% url "doc_view" name=doc.name %}">Cancel</a>
|
||||
<input class="button" type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<form role="form" enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% block content_end %}
|
||||
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
|
||||
<script type="text/javascript" src="/js/tokenized-field.js"></script>
|
||||
|
||||
{% block js %}
|
||||
<script src="/facelift/js/lib/select2-3.5.2.min.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -17,7 +17,7 @@ Change the document shepherd email for {{ doc.name }}-{{ doc.rev }}
|
|||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a class="button" href="{% url "doc_view" name=doc.name %}">Cancel</a>
|
||||
<a class="button" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
<input class="button" type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,46 +1,24 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block title %}Change State: {{doc.title}}{% endblock %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% 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 title %}Change state for {{doc.title}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change State: {{doc.title}}</h1>
|
||||
<h1>Change state<br><small>{{doc.title}}</small></h1>
|
||||
|
||||
<p class="helptext">For help on the states, see the <a href="{{help_url}}">state table</a>.</p>
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<form class="change-state" action="" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}</th>
|
||||
<td>{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
<p><a class="btn btn-info" href="{{help_url}}">Help on states</a></p>
|
||||
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="2" class="actions">
|
||||
<a href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,42 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "ietf.html" %}
|
||||
|
||||
{% block morecss %}
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
color: #a00;
|
||||
}
|
||||
form.edit-info #id_title {
|
||||
width: 600px;
|
||||
}
|
||||
{% endblock %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}
|
||||
Change the title for {{titletext}}
|
||||
{% endblock %}
|
||||
{% block title %}Change title for {{doc.title}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change the title for {{titletext}}</h1>
|
||||
<h1>Change title<br><small>{{titletext}}</small></h1>
|
||||
|
||||
<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 }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
|
||||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url "doc_view" name=doc.canonical_name %}">Back</a>
|
||||
<input type="submit" value="Submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% bootstrap_messages %}
|
||||
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a class="btn btn-default pull-right" href="{% url "doc_view" name=doc.canonical_name %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue