Merged in more shim-layer removals from olau@iola.dk

(-r5465:HEAD from branch/iola/shimfree).  Copying relevant
commit messages here:

 - Deleted dead code in many places.

 - Renamed id-something to draft-something, make the "is
   this eligible for expiration" logic clearer

 - Added a name for IPR search URL

 - Revamped the ballot popup view

 - URL reversed the IPR search link instead of hardcoding it

 - Cleaned up search views and remove dead code, port them to the new DB
   schema, hack related views in iesg/ and wginfo/ to use the new search
   interfaces, avoid camelCase in search GET parameters (with
   backwards-compat fallback), add some simple search unit tests, remove
   caching from views_search.py index pages as they're now pretty fast to
   generate, rewrite ballot popup JS, regularize some CSS classes to use
   hyphen-notation rather than camelCase, move some of the search
   templates to doc/.

   idrfc/ now mostly contains some wrapper code still in use by other
   subdirs, some ported code not yet moved, and dead code.

 - Fixed output bug in test crawler and print referrer upon errors so it's
   easier to figure out where a link came from

 - Added /doc/in-last-call/ to crawler, report original page as referrer in
   a redirect chain rather than intermediate URL

 - Ported idindex to new schema, speed them up, add tests, refactor index
   page in views_search to share code with the text index file, get rid
   of some special-case idindex filters from ietf_filters, move
   "/drafts/" redirects to a file in /doc/

 - Ported /idtracker/status/ and /idtracker/status/last-call/ overview of
   drafts in IESG process to new schema in /doc/iesg/ and
   /doc/iesg/last-call/

 - Added redirects for all of /idtracker/*, removed all view code and other
   dead code from idtracker/

 - Removed the idtracker sitemap indexing drafts - in its current form,
   it adds nothing of value to the HTML-based /doc/all/ view, and it's
   pretty slow

 - Ported idtracker feeds to new schema, move them to doc/, cleaned up
   idtracker/ - only templatetags/ietf_filters and proxy code is left
 - Legacy-Id: 5836
This commit is contained in:
Henrik Levkowetz 2013-07-17 20:52:39 +00:00
commit cbfe489ff5
97 changed files with 1862 additions and 2194 deletions

View file

@ -231,7 +231,7 @@ Regards,
Henrik
(via the mkrelease script)
" > ~/src/db/release-mail-v$VER.txt
cat ~/src/db/release-mail-v$VER.txt | $do mail -s "New datatracker release: v$VER" -c henrik@levkowetz.com -c glen@amsl.com -c fenner@fenron.net -c rjs@nostrum.com -c housley@vigilsec.com -c cmorgan@amsl.com -c avezza@amsl.com -c amorris@amsl.com -c smccammon@amsl.com -c kmoreland@amsl.com -c stevey@amsl.com -c wchen@amsl.com -c olau@iola.dk $contributors codesprints@ietf.org
cat ~/src/db/release-mail-v$VER.txt | $do mail -s "New datatracker release: v$VER" -c henrik@levkowetz.com -c glen@amsl.com -c fenner@fenron.net -c rjs@nostrum.com -c housley@vigilsec.com -c cmorgan@amsl.com -c avezza@amsl.com -c amorris@amsl.com -c smccammon@amsl.com -c kmoreland@amsl.com -c stevey@amsl.com -c olau@iola.dk $contributors codesprints@ietf.org
$do toolsfeed control changelog /www/tools.ietf.org/tools/atomfeed.xml
$do toolpush /www/tools.ietf.org/tools/atomfeed.xml

View file

@ -11,10 +11,11 @@ syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER)
from ietf.idrfc.expire import *
if not in_id_expire_freeze():
for doc in get_expired_ids():
send_expire_notice_for_id(doc)
expire_id(doc)
syslog.syslog("Expired %s (id=%s)%s" % (doc.file_tag(), doc.pk, " in the ID Tracker" if doc.latest_event(type="started_iesg_process") else ""))
if not in_draft_expire_freeze():
for doc in get_expired_drafts():
send_expire_notice_for_draft(doc)
expire_draft(doc)
syslog.syslog("Expired draft %s-%s" % (doc.name, doc.rev))
clean_up_id_files()
syslog.syslog("Cleaning up draft files")
clean_up_draft_files()

View file

@ -6,11 +6,11 @@ from ietf import settings
from django.core import management
management.setup_environ(settings)
from ietf.idrfc.expire import get_soon_to_expire_ids, send_expire_warning_for_id
from ietf.idrfc.expire import get_soon_to_expire_drafts, send_expire_warning_for_draft
# notify about documents that expire within the next 2 weeks
notify_days = 14
notify_days = 14
for doc in get_soon_to_expire_ids(notify_days):
send_expire_warning_for_id(doc)
for doc in get_soon_to_expire_drafts(notify_days):
send_expire_warning_for_draft(doc)

View file

@ -24,6 +24,12 @@ connection.queries = DontSaveQueries()
MAX_URL_LENGTH = 500
SLOW_THRESHOLD = 1.0
initial = ["/doc/all/", "/doc/in-last-call/"]
visited = set()
urls = {} # url -> referrer
def strip_url(url):
if url.startswith("http://testserver"):
url = url[len("http://testserver"):]
@ -40,15 +46,13 @@ def extract_html_urls(content):
yield url
visited = set()
blacklist = set()
urls = set(["/doc/all/"])
client = django.test.Client()
for url in initial:
urls[url] = "[initial]"
while urls:
url = urls.pop()
url, referrer = urls.popitem()
visited.add(url)
@ -62,7 +66,7 @@ while urls:
except:
print "FAIL", url
print "============="
traceback.print_exc()
print traceback.format_exc()
print "============="
else:
tags = []
@ -70,7 +74,7 @@ while urls:
if r.status_code in (301, 302):
u = strip_url(r["Location"])
if u not in visited and u not in urls:
urls.add(u)
urls[u] = referrer # referrer is original referrer, not redirected url
elif r.status_code == 200:
ctype = r["Content-Type"]
@ -80,9 +84,9 @@ while urls:
if ctype == "text/html":
for u in extract_html_urls(r.content):
if u not in visited and u not in urls:
urls.add(u)
urls[u] = url
else:
tags.append("FAIL")
tags.append(u"FAIL (from %s)" % referrer)
if elapsed.total_seconds() > SLOW_THRESHOLD:
tags.append("SLOW")

86
ietf/doc/feeds.py Normal file
View file

@ -0,0 +1,86 @@
# Copyright The IETF Trust 2007, All Rights Reserved
import datetime, re
from django.conf import settings
from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
from django.utils.feedgenerator import Atom1Feed
from django.core.urlresolvers import reverse as urlreverse
from django.template.defaultfilters import truncatewords_html, date as datefilter, linebreaks
from django.utils.html import strip_tags
from django.utils.text import truncate_words
from ietf.doc.models import *
from ietf.doc.utils import augment_events_with_revision
from ietf.idtracker.templatetags.ietf_filters import format_textarea
class DocumentChanges(Feed):
feed_type = Atom1Feed
def get_object(self, bits):
if len(bits) != 1:
raise Document.DoesNotExist
return Document.objects.get(docalias__name=bits[0])
def title(self, obj):
return "Changes for %s" % obj.display_name()
def link(self, obj):
if obj is None:
raise FeedDoesNotExist
if not hasattr(self, "cached_link"):
self.cached_link = urlreverse("doc_history", kwargs=dict(name=obj.canonical_name()))
return self.cached_link
def subtitle(self, obj):
return "History of change entries for %s." % obj.display_name()
def items(self, obj):
events = obj.docevent_set.all().order_by("-time","-id")
augment_events_with_revision(obj, events)
return events
def item_title(self, item):
return u"[%s] %s [rev. %s]" % (item.by, truncate_words(strip_tags(item.desc), 15), item.rev)
def item_description(self, item):
return truncatewords_html(format_textarea(item.desc), 20)
def item_pubdate(self, item):
return item.time
def item_author_name(self, item):
return unicode(item.by)
def item_link(self, item):
return self.cached_link + "#history-%s" % item.pk
class InLastCall(Feed):
title = "Documents in Last Call"
subtitle = "Announcements for documents in last call."
feed_type = Atom1Feed
author_name = 'IESG Secretary'
link = "/doc/iesg/last-call/"
def items(self):
docs = list(Document.objects.filter(type="draft", states=State.objects.get(type="draft-iesg", slug="lc")))
for d in docs:
d.lc_event = d.latest_event(LastCallDocEvent, type="sent_last_call")
docs = [d for d in docs if d.lc_event]
docs.sort(key=lambda d: d.lc_event.expires)
return docs
def item_title(self, item):
return u"%s (%s - %s)" % (item.name,
datefilter(item.lc_event.time, "F j"),
datefilter(item.lc_event.expires, "F j, Y"))
def item_description(self, item):
return linebreaks(item.lc_event.desc)
def item_pubdate(self, item):
return item.lc_event.time

View file

@ -37,6 +37,9 @@ class State(models.Model):
class Meta:
ordering = ["type", "order"]
IESG_BALLOT_ACTIVE_STATES = ("lc", "writeupw", "goaheadw", "iesg-eva", "defer")
IESG_SUBSTATE_TAGS = ('point', 'ad-f-up', 'need-rev', 'extpty')
class DocumentInfo(models.Model):
"""Any kind of document. Draft, RFC, Charter, IPR Statement, Liaison Statement"""
time = models.DateTimeField(default=datetime.datetime.now) # should probably have auto_now=True
@ -186,9 +189,7 @@ class Document(DocumentInfo):
def get_absolute_url(self):
name = self.name
if self.type_id == "draft" and self.get_state_slug() == "rfc":
aliases = self.docalias_set.filter(name__startswith="rfc")
if aliases:
name = aliases[0].name
name = self.canonical_name()
elif self.type_id in ('slides','agenda','minutes'):
session = self.session_set.all()[0]
meeting = session.meeting
@ -260,11 +261,10 @@ class Document(DocumentInfo):
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
return DocAlias.objects.filter(relateddocument__source=self, relateddocument__relationship__in=relationship)
#TODO can/should this be a function instead of a property? Currently a view uses it as a property
@property
def telechat_date(self):
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
return e.telechat_date if e else None
def telechat_date(self, e=None):
if not e:
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
return e.telechat_date if e and e.telechat_date and e.telechat_date >= datetime.date.today() else None
def area_acronym(self):
g = self.group
@ -283,10 +283,6 @@ class Document(DocumentInfo):
else:
return "none"
def on_upcoming_agenda(self):
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
return bool(e and e.telechat_date and e.telechat_date >= datetime.date.today())
def returning_item(self):
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
return e.returning_item if e else None
@ -311,56 +307,56 @@ class Document(DocumentInfo):
return '<a href="%s">%s-%s</a>' % (self.get_absolute_url(), self.name , self.rev)
def rfc_number(self):
qs = self.docalias_set.filter(name__startswith='rfc')
return qs[0].name[3:] if qs else None
n = self.canonical_name()
return n[3:] if n.startswith("rfc") else None
def friendly_state(self):
""" Return a concise text description of the document's current state """
if self.type_id=='draft':
# started_iesg_process is is how the redesigned database schema (as of May2012) captured what
# used to be "has an IDInternal", aka *Wrapper.in_ietf_process()=True
in_iesg_process = self.latest_event(type='started_iesg_process')
iesg_state_summary=None
if in_iesg_process:
iesg_state = self.states.get(type='draft-iesg')
""" Return a concise text description of the document's current state."""
state = self.get_state()
if not state:
return "Unknown state"
if self.type_id == 'draft':
iesg_state = self.get_state("draft-iesg")
iesg_state_summary = None
if iesg_state:
# This knowledge about which tags are reportable IESG substate tags is duplicated in idrfc
IESG_SUBSTATE_TAGS = ('point', 'ad-f-up', 'need-rev', 'extpty')
iesg_substate = self.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
# There really shouldn't be more than one tag in iesg_substate, but this will do something sort-of-sensible if there is
iesg_state_summary = iesg_state.name
if iesg_substate:
iesg_state_summary = iesg_state_summary + "::"+"::".join(tag.name for tag in iesg_substate)
if self.get_state_slug() == "rfc":
n = self.rfc_number()
return "<a href=\"%s\">RFC %s</a>" % (urlreverse('doc_view', kwargs=dict(name='rfc%s' % n)), n)
elif self.get_state_slug() == "repl":
if state.slug == "rfc":
return "RFC %s (%s)" % (self.rfc_number(), self.std_level)
elif state.slug == "repl":
rs = self.related_that("replaces")
if rs:
return mark_safe("Replaced by " + ", ".join("<a href=\"%s\">%s</a>" % (urlreverse('doc_view', args=[name]), name) for name in rs))
return mark_safe("Replaced by " + ", ".join("<a href=\"%s\">%s</a>" % (urlreverse('doc_view', kwargs=dict(name=name)), name) for name in rs))
else:
return "Replaced"
elif self.get_state_slug() == "active":
if in_iesg_process:
elif state.slug == "active":
if iesg_state:
if iesg_state.slug == "dead":
# Many drafts in the draft-iesg "Dead" state are not dead
# in other state machines; they're just not currently under
# IESG processing. Show them as "I-D Exists (IESG: Dead)" instead...
return "I-D Exists (IESG: "+iesg_state_summary+")"
return "I-D Exists (IESG: %s)" % iesg_state_summary
elif iesg_state.slug == "lc":
expiration_date = str(self.latest_event(LastCallDocEvent,type="sent_last_call").expires.date())
return iesg_state_summary + " (ends "+expiration_date+")"
else:
return iesg_state_summary
e = self.latest_event(LastCallDocEvent, type="sent_last_call")
if e:
return iesg_state_summary + " (ends %s)" % e.expires.date().isoformat()
return iesg_state_summary
else:
return "I-D Exists"
else:
if in_iesg_process and iesg_state.slug == "dead":
return self.get_state().name +" (IESG: "+iesg_state_summary+")"
if iesg_state and iesg_state.slug == "dead":
return state.name + " (IESG: %s)" % iesg_state_summary
# Expired/Withdrawn by Submitter/IETF
return self.get_state().name
return state.name
else:
return self.get_state().name
return state.name
def ipr(self):
"""Returns the IPR disclosures against this document (as a queryset over IprDocAlias)."""

View file

@ -0,0 +1,23 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf import settings
from django.conf.urls.defaults import patterns
from django.http import HttpResponsePermanentRedirect
from django.shortcuts import get_object_or_404
from ietf.group.models import Group
urlpatterns = patterns('',
(r'^$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/'}),
(r'^all/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/all/'}),
(r'^rfc/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/all/#rfc'}),
(r'^dead/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/all/#expired'}),
(r'^current/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/active/'}),
(r'^(?P<object_id>\d+)/(related/)?$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/' }),
(r'^(?P<name>[^/]+)/(related/)?$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/%(name)s/' }),
(r'^wgid/(?P<id>\d+)/$', lambda request, id: HttpResponsePermanentRedirect("/wg/%s/" % get_object_or_404(Group, id=id).acronym)),
(r'^wg/(?P<acronym>[^/]+)/$', 'django.views.generic.simple.redirect_to', { 'url': '/wg/%(acronym)s/' }),
(r'^all_id(?:_txt)?.html$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/id/all_id.txt' }),
)

View file

@ -0,0 +1,13 @@
from django.conf.urls.defaults import patterns, url
from django.views.generic.simple import redirect_to
urlpatterns = patterns('',
(r'^help/(?:sub)?state/(?:\d+/)?$', redirect_to, {'url': '/doc/help/state/draft-iesg/' }),
(r'^help/evaluation/$', redirect_to, {'url':'http://www.ietf.org/iesg/voting-procedures.html' }),
(r'^status/$', redirect_to, {'url':'/doc/iesg/' }),
(r'^status/last-call/$', redirect_to, {'url':'/doc/iesg/last-call/' }),
(r'^rfc0*(?P<rfc_number>\d+)/$', redirect_to, {'url':'/doc/rfc%(rfc_number)s/' }),
(r'^(?P<name>[^/]+)/$', redirect_to, {'url':'/doc/%(name)s/' }),
(r'^(?P<name>[^/]+)/comment/\d+/$', redirect_to, {'url':'/doc/%(name)s/history/' }),
(r'^$', redirect_to, { 'url': '/doc/'}),
)

View file

@ -2,7 +2,6 @@ import os, shutil, datetime
import django.test
from django.core.urlresolvers import reverse as urlreverse
from django.conf import settings
from pyquery import PyQuery
@ -21,6 +20,108 @@ from ietf.iesg.models import TelechatDate
from ietf.doc.tests_conflict_review import *
class SearchTestCase(django.test.TestCase):
fixtures = ['names']
def test_search(self):
draft = make_test_data()
base_url = urlreverse("doc_search")
# only show form, no search yet
r = self.client.get(base_url)
self.assertEqual(r.status_code, 200)
# no match
r = self.client.get(base_url + "?activedrafts=on&name=thisisnotadocumentname")
self.assertEqual(r.status_code, 200)
self.assertTrue("no documents match" in str(r.content).lower())
r = self.client.get(base_url + "?rfcs=on&name=xyzzy")
self.assertEqual(r.status_code, 200)
self.assertTrue("no documents match" in r.content.lower())
r = self.client.get(base_url + "?olddrafts=on&name=")
self.assertEqual(r.status_code, 200)
self.assertTrue("no documents match" in r.content.lower())
# find by rfc/active/inactive
draft.set_state(State.objects.get(type="draft", slug="rfc"))
r = self.client.get(base_url + "?rfcs=on&name=%s" % draft.name)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
draft.set_state(State.objects.get(type="draft", slug="active"))
r = self.client.get(base_url + "?activedrafts=on&name=%s" % draft.name)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
draft.set_state(State.objects.get(type="draft", slug="expired"))
r = self.client.get(base_url + "?olddrafts=on&name=%s" % draft.name)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
draft.set_state(State.objects.get(type="draft", slug="active"))
# find by title
r = self.client.get(base_url + "?activedrafts=on&name=%s" % draft.title.split()[0])
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
# find by author
r = self.client.get(base_url + "?activedrafts=on&by=author&author=%s" % draft.authors.all()[0].person.name_parts()[1])
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
# find by group
r = self.client.get(base_url + "?activedrafts=on&by=group&group=%s" % draft.group.acronym)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
# find by area
r = self.client.get(base_url + "?activedrafts=on&by=area&area=%s" % draft.group.parent_id)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
# find by area
r = self.client.get(base_url + "?activedrafts=on&by=area&area=%s" % draft.group.parent_id)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
# find by AD
r = self.client.get(base_url + "?activedrafts=on&by=ad&ad=%s" % draft.ad_id)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
# find by IESG state
r = self.client.get(base_url + "?activedrafts=on&by=state&state=%s&substate=" % draft.get_state("draft-iesg").pk)
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
def test_drafts_pages(self):
draft = make_test_data()
r = self.client.get(urlreverse("drafts_for_ad", kwargs=dict(name=draft.ad.full_name_as_key())))
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
draft.set_state(State.objects.get(type="draft-iesg", slug="lc"))
r = self.client.get(urlreverse("drafts_in_last_call"))
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
def test_indexes(self):
draft = make_test_data()
r = self.client.get(urlreverse("index_all_drafts"))
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.name in r.content)
r = self.client.get(urlreverse("index_active_drafts"))
self.assertEqual(r.status_code, 200)
self.assertTrue(draft.title in r.content)
class DocTestCase(django.test.TestCase):
fixtures = ['names']
@ -134,7 +235,7 @@ class DocTestCase(django.test.TestCase):
self.assertEqual(r.status_code, 200)
# test popup too while we're at it
r = self.client.get(urlreverse("ietf.doc.views_doc.ballot_for_popup", kwargs=dict(name=doc.name)))
r = self.client.get(urlreverse("ietf.doc.views_doc.ballot_popup", kwargs=dict(name=doc.name, ballot_id=ballot.pk)))
self.assertEqual(r.status_code, 200)
def test_document_json(self):

View file

@ -222,7 +222,7 @@ def document_main(request, name, rev=None):
# ballot
ballot_summary = None
if iesg_state and iesg_state.slug in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"):
if iesg_state and iesg_state.slug in IESG_BALLOT_ACTIVE_STATES:
active_ballot = doc.active_ballot()
if active_ballot:
ballot_summary = needed_ballot_positions(doc, active_ballot.active_ad_positions().values())
@ -653,6 +653,17 @@ def document_ballot(request, name, ballot_id=None):
),
context_instance=RequestContext(request))
def ballot_popup(request, name, ballot_id):
doc = get_object_or_404(Document, docalias__name=name)
c = document_ballot_content(request, doc, ballot_id=ballot_id, editable=False)
return render_to_response("doc/ballot_popup.html",
dict(doc=doc,
ballot_content=c,
ballot_id=ballot_id,
),
context_instance=RequestContext(request))
def document_json(request, name):
doc = get_object_or_404(Document, docalias__name=name)
@ -699,11 +710,6 @@ def document_json(request, name):
return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain')
def ballot_for_popup(request, name):
doc = get_object_or_404(Document, docalias__name=name)
return HttpResponse(document_ballot_content(request, doc, ballot_id=None, editable=False))
def ballot_json(request, name):
# REDESIGN: this view needs to be deleted or updated
def get_ballot(name):

45
ietf/doc/views_help.py Normal file
View file

@ -0,0 +1,45 @@
from django import forms
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from ietf.doc.models import *
def state_help(request, type):
slug, title = {
"draft-iesg": ("draft-iesg", "IESG States For Internet-Drafts"),
"draft-rfceditor": ("draft-rfceditor", "RFC Editor States For Internet-Drafts"),
"draft-iana-action": ("draft-iana-action", "IANA Action States For Internet-Drafts"),
"charter": ("charter", "Charter States"),
"conflict-review": ("conflrev", "Conflict Review States")
}.get(type, "")
state_type = get_object_or_404(StateType, slug=slug)
states = State.objects.filter(type=state_type).order_by("order")
has_next_states = False
for state in states:
if state.next_states.all():
has_next_states = True
break
tags = []
if state_type.slug == "draft-iesg":
# legacy hack
states = list(states)
fake_state = dict(name="I-D Exists",
desc="Initial (default) state for all internet drafts. Such documents are not being tracked by the IESG as no request has been made of the IESG to do anything with the document.",
next_states=dict(all=State.objects.filter(type="draft-iesg", slug__in=("watching", "pub-req")))
)
states.insert(0, fake_state)
tags = DocTagName.objects.filter(slug__in=IESG_SUBSTATE_TAGS)
return render_to_response("doc/state_help.html", {
"title": title,
"state_type": state_type,
"states": states,
"has_next_states": has_next_states,
"tags": tags,
},
context_instance=RequestContext(request))

View file

@ -34,5 +34,5 @@ from ietf import settings
from django.core import management
management.setup_environ(settings)
from ietf.idindex.views import all_id2_txt
from ietf.idindex.index import all_id2_txt
print all_id2_txt().encode('utf-8'),

View file

@ -34,5 +34,5 @@ from ietf import settings
from django.core import management
management.setup_environ(settings)
from ietf.idindex.views import all_id_txt
print all_id_txt(),
from ietf.idindex.index import all_id_txt
print all_id_txt().encode("utf-8"),

View file

@ -34,9 +34,5 @@ from ietf import settings
from django.core import management
management.setup_environ(settings)
from ietf.idindex.views import id_abstracts_txt
x = id_abstracts_txt()
if isinstance(x, unicode):
print x.encode('utf-8'),
else:
print x,
from ietf.idindex.index import id_index_txt
print id_index_txt(with_abstracts=True).encode('utf-8'),

View file

@ -34,9 +34,5 @@ from ietf import settings
from django.core import management
management.setup_environ(settings)
from ietf.idindex.views import id_index_txt
x = id_index_txt()
if isinstance(x, unicode):
print x.encode('utf-8'),
else:
print x,
from ietf.idindex.index import id_index_txt
print id_index_txt().encode('utf-8'),

275
ietf/idindex/index.py Normal file
View file

@ -0,0 +1,275 @@
# code to generate plain-text index files that are placed on
# www.ietf.org in the same directory as the I-Ds
import datetime, os
import pytz
from django.conf import settings
from django.template.loader import render_to_string
from ietf.idtracker.templatetags.ietf_filters import clean_whitespace
from ietf.doc.models import *
def all_id_txt():
# this returns a lot of data so try to be efficient
# precalculations
revision_time = dict(NewRevisionDocEvent.objects.filter(type="new_revision", doc__name__startswith="draft-").order_by('time').values_list("doc_id", "time"))
def formatted_rev_date(name):
t = revision_time.get(name)
return t.strftime("%Y-%m-%d") if t else ""
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc",
document__states=State.objects.get(type="draft", slug="rfc")).values_list("document_id", "name"))
replacements = dict(RelatedDocument.objects.filter(target__document__states=State.objects.get(type="draft", slug="repl"),
relationship="replaces").values_list("target__document_id", "source"))
# we need a distinct to prevent the queries below from multiplying the result
all_ids = Document.objects.filter(type="draft").order_by('name').exclude(name__startswith="rfc").distinct()
res = ["\nInternet-Drafts Status Summary\n"]
def add_line(f1, f2, f3, f4):
# each line must have exactly 4 tab-separated fields
res.append(f1 + "\t" + f2 + "\t" + f3 + "\t" + f4)
inactive_states = ["pub", "watching", "dead"]
in_iesg_process = all_ids.exclude(states=State.objects.get(type="draft", slug="rfc")).filter(states__in=list(State.objects.filter(type="draft-iesg").exclude(slug__in=inactive_states))).only("name", "rev")
# handle those actively in the IESG process
for d in in_iesg_process:
state = d.get_state("draft-iesg").name
tags = d.tags.filter(slug__in=IESG_SUBSTATE_TAGS).values_list("name", flat=True)
if tags:
state += "::" + "::".join(tags)
add_line(d.name + "-" + d.rev,
formatted_rev_date(d.name),
"In IESG processing - ID Tracker state <" + state + ">",
"",
)
# handle the rest
not_in_process = all_ids.exclude(pk__in=[d.name for d in in_iesg_process])
for s in State.objects.filter(type="draft").order_by("order"):
for name, rev in not_in_process.filter(states=s).values_list("name", "rev"):
state = s.name
last_field = ""
if s.slug == "rfc":
a = rfc_aliases.get(name)
if a:
last_field = a[3:]
elif s.slug == "repl":
state += " replaced by " + replacements.get(name, "0")
add_line(name + "-" + rev,
formatted_rev_date(name),
state,
last_field,
)
return u"\n".join(res) + "\n"
def file_types_for_drafts():
"""Look in the draft directory and return file types found as dict (name + rev -> [t1, t2, ...])."""
file_types = {}
for filename in os.listdir(settings.INTERNET_DRAFT_PATH):
if filename.startswith("draft-"):
base, ext = os.path.splitext(filename)
if ext:
if base not in file_types:
file_types[base] = [ext]
else:
file_types[base].append(ext)
return file_types
def all_id2_txt():
# this returns a lot of data so try to be efficient
drafts = Document.objects.filter(type="draft").exclude(name__startswith="rfc").order_by('name').select_related('group', 'group__parent', 'ad', 'ad__email', 'intended_std_level', 'shepherd', 'shepherd__email')
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc",
document__states=State.objects.get(type="draft", slug="rfc")).values_list("document_id", "name"))
replacements = dict(RelatedDocument.objects.filter(target__document__states=State.objects.get(type="draft", slug="repl"),
relationship="replaces").values_list("target__document_id", "source"))
revision_time = dict(DocEvent.objects.filter(type="new_revision", doc__name__startswith="draft-").order_by('time').values_list("doc_id", "time"))
file_types = file_types_for_drafts()
authors = {}
for a in DocumentAuthor.objects.filter(document__name__startswith="draft-").order_by("order").select_related("author", "author__person").iterator():
if a.document_id not in authors:
l = authors[a.document_id] = []
else:
l = authors[a.document_id]
if "@" in a.author.address:
l.append(u'%s <%s>' % (a.author.person.plain_name().replace("@", ""), a.author.address.replace(",", "")))
else:
l.append(a.author.person.plain_name())
res = []
for d in drafts:
state = d.get_state_slug()
iesg_state = d.get_state("draft-iesg")
fields = []
# 0
fields.append(d.name + "-" + d.rev)
# 1
fields.append("-1") # used to be internal numeric identifier, we don't have that anymore
# 2
fields.append(d.get_state().name if state else "")
# 3
if state == "active":
s = "I-D Exists"
if iesg_state:
s = iesg_state.name
tags = d.tags.filter(slug__in=IESG_SUBSTATE_TAGS).values_list("name", flat=True)
if tags:
s += "::" + "::".join(tags)
fields.append(s)
else:
fields.append("")
# 4
rfc_number = ""
if state == "rfc":
a = rfc_aliases.get(d.name)
if a:
rfc_number = a[3:]
fields.append(rfc_number)
# 5
repl = ""
if state == "repl":
repl = replacements.get(d.name, "")
fields.append(repl)
# 6
t = revision_time.get(d.name)
fields.append(t.strftime("%Y-%m-%d") if t else "")
# 7
group_acronym = ""
if d.group and d.group.type_id != "area" and d.group.acronym != "none":
group_acronym = d.group.acronym
fields.append(group_acronym)
# 8
area = ""
if d.group:
if d.group.type_id == "area":
area = d.group.acronym
elif d.group.type_id == "wg" and d.group.parent and d.group.parent.type_id == "area":
area = d.group.parent.acronym
fields.append(area)
# 9 responsible AD name
fields.append(unicode(d.ad) if d.ad else "")
# 10
fields.append(d.intended_std_level.name if d.intended_std_level else "")
# 11
lc_expires = ""
if iesg_state and iesg_state.slug == "lc":
e = d.latest_event(LastCallDocEvent, type="sent_last_call")
if e:
lc_expires = e.expires.strftime("%Y-%m-%d")
fields.append(lc_expires)
# 12
doc_file_types = file_types.get(d.name + "-" + d.rev, [])
doc_file_types.sort() # make the order consistent (and the result testable)
fields.append(",".join(doc_file_types) if state == "active" else "")
# 13
fields.append(clean_whitespace(d.title)) # FIXME: we should make sure this is okay in the database and in submit
# 14
fields.append(u", ".join(authors.get(d.name, [])))
# 15
fields.append(d.shepherd.formatted_email().replace('"', '') if d.shepherd else "")
# 16 Responsible AD name and email
fields.append(d.ad.formatted_email().replace('"', '') if d.ad else "")
#
res.append(u"\t".join(fields))
return render_to_string("idindex/all_id2.txt", {'data': u"\n".join(res) })
def active_drafts_index_by_group(extra_values=()):
"""Return active drafts grouped into their corresponding
associated group, for spitting out draft index."""
# this returns a lot of data so try to be efficient
active_state = State.objects.get(type="draft", slug="active")
groups_dict = dict((g.id, g) for g in Group.objects.all())
extracted_values = ("name", "rev", "title", "group_id") + extra_values
docs_dict = dict((d["name"], d)
for d in Document.objects.filter(states=active_state).values(*extracted_values))
# add initial and latest revision time
for time, doc_id in NewRevisionDocEvent.objects.filter(type="new_revision", doc__states=active_state).order_by('-time').values_list("time", "doc_id"):
d = docs_dict.get(doc_id)
if d:
if "rev_time" not in d:
d["rev_time"] = time
d["initial_rev_time"] = time
# add authors
for a in DocumentAuthor.objects.filter(document__states=active_state).order_by("order").select_related("author__person"):
d = docs_dict.get(a.document_id)
if d:
if "authors" not in d:
d["authors"] = []
d["authors"].append(unicode(a.author.person))
# put docs into groups
for d in docs_dict.itervalues():
g = groups_dict.get(d["group_id"])
if not g:
continue
if not hasattr(g, "active_drafts"):
g.active_drafts = []
g.active_drafts.append(d)
groups = [g for g in groups_dict.itervalues() if hasattr(g, "active_drafts")]
groups.sort(key=lambda g: g.acronym)
fallback_time = datetime.datetime(1950, 1, 1)
for g in groups:
g.active_drafts.sort(key=lambda d: d.get("initial_rev_time", fallback_time))
return groups
def id_index_txt(with_abstracts=False):
extra_values = ()
if with_abstracts:
extra_values = ("abstract",)
groups = active_drafts_index_by_group(extra_values)
file_types = file_types_for_drafts()
for g in groups:
for d in g.active_drafts:
# we need to output a multiple extension thing
types = file_types.get(d["name"] + "-" + d["rev"], "")
exts = ".txt"
if ".ps" in types:
exts += ",.ps"
if ".pdf" in types:
exts += ",.pdf"
d["exts"] = exts
return render_to_string("idindex/id_index.txt", {
'groups': groups,
'time': datetime.datetime.now(pytz.UTC).strftime("%Y-%m-%d %H:%M:%S %Z"),
'with_abstracts': with_abstracts,
})

View file

@ -1 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved

View file

@ -1,72 +1,146 @@
# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# * Neither the name of the Nokia Corporation and/or its
# subsidiary(-ies) nor the names of its contributors may be used
# to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import datetime, shutil
import unittest
import re
from django.test.client import Client
from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest
import django.test
from django.core.urlresolvers import reverse as urlreverse
class IdIndexUrlTestCase(SimpleUrlTestCase):
def testUrls(self):
self.doTestUrls(__file__)
from ietf.utils.test_data import make_test_data
# class IndexTestCase(unittest.TestCase, RealDatabaseTest):
# def setUp(self):
# self.setUpRealDatabase()
# def tearDown(self):
# self.tearDownRealDatabase()
from ietf.doc.models import *
from ietf.idindex.index import *
# def testAllId(self):
# print " Testing all_id.txt generation"
# c = Client()
# response = c.get('/drafts/_test/all_id.txt')
# self.assertEquals(response.status_code, 200)
# content = response.content
# # Test that correct version number is shown for couple of old drafts
# self.assert_(content.find("draft-ietf-tls-psk-09") >= 0)
# self.assert_(content.find("draft-eronen-eap-sim-aka-80211-00") >= 0)
# # Since all_id.txt contains all old drafts, it should never shrink
# lines = content.split("\n")
# self.assert_(len(lines) > 18000)
# # Test that the lines look OK and have correct number of tabs
# r = re.compile(r'^(draft-\S*-\d\d)\t(\d\d\d\d-\d\d-\d\d)\t([^\t]+)\t([^\t]*)$')
# for line in lines:
# if ((line == "") or
# (line == "Internet-Drafts Status Summary") or
# (line == "Web version is available at") or
# (line == "https://datatracker.ietf.org/public/idindex.cgi")):
# pass
# elif r.match(line):
# pass
# else:
# self.fail("Unexpected line \""+line+"\"")
# print "OK (all_id.txt)"
class IndexTestCase(django.test.TestCase):
fixtures = ['names']
def setUp(self):
self.id_dir = os.path.abspath("tmp-id-dir")
os.mkdir(self.id_dir)
settings.INTERNET_DRAFT_PATH = self.id_dir
def tearDown(self):
shutil.rmtree(self.id_dir)
def write_draft_file(self, name, size):
with open(os.path.join(self.id_dir, name), 'w') as f:
f.write("a" * size)
def test_all_id_txt(self):
draft = make_test_data()
# active in IESG process
draft.set_state(State.objects.get(type="draft", slug="active"))
draft.set_state(State.objects.get(type="draft-iesg", slug="lc"))
txt = all_id_txt()
self.assertTrue(draft.name + "-" + draft.rev in txt)
self.assertTrue(draft.get_state("draft-iesg").name in txt)
# not active in IESG process
draft.unset_state("draft-iesg")
txt = all_id_txt()
self.assertTrue(draft.name + "-" + draft.rev in txt)
self.assertTrue("Active" in txt)
# published
draft.set_state(State.objects.get(type="draft", slug="rfc"))
DocAlias.objects.create(name="rfc1234", document=draft)
txt = all_id_txt()
self.assertTrue(draft.name + "-" + draft.rev in txt)
self.assertTrue("RFC\t1234" in txt)
# replaced
draft.set_state(State.objects.get(type="draft", slug="repl"))
RelatedDocument.objects.create(
relationship=DocRelationshipName.objects.get(slug="replaces"),
source=Document.objects.create(type_id="draft", rev="00", name="draft-test-replacement"),
target=draft.docalias_set.get(name__startswith="draft"))
txt = all_id_txt()
self.assertTrue(draft.name + "-" + draft.rev in txt)
self.assertTrue("Replaced replaced by draft-test-replacement" in txt)
def test_all_id2_txt(self):
draft = make_test_data()
def get_fields(content):
self.assertTrue(draft.name + "-" + draft.rev in content)
for line in content.splitlines():
if line.startswith(draft.name + "-" + draft.rev):
return line.split("\t")
# test Active
draft.set_state(State.objects.get(type="draft", slug="active"))
draft.set_state(State.objects.get(type="draft-iesg", slug="review-e"))
NewRevisionDocEvent.objects.create(doc=draft, type="new_revision", rev=draft.rev, by=draft.ad)
self.write_draft_file("%s-%s.txt" % (draft.name, draft.rev), 5000)
self.write_draft_file("%s-%s.pdf" % (draft.name, draft.rev), 5000)
t = get_fields(all_id2_txt())
self.assertEqual(t[0], draft.name + "-" + draft.rev)
self.assertEqual(t[1], "-1")
self.assertEqual(t[2], "Active")
self.assertEqual(t[3], "Expert Review")
self.assertEqual(t[4], "")
self.assertEqual(t[5], "")
self.assertEqual(t[6], draft.latest_event(type="new_revision").time.strftime("%Y-%m-%d"))
self.assertEqual(t[7], draft.group.acronym)
self.assertEqual(t[8], draft.group.parent.acronym)
self.assertEqual(t[9], unicode(draft.ad))
self.assertEqual(t[10], draft.intended_std_level.name)
self.assertEqual(t[11], "")
self.assertEqual(t[12], ".pdf,.txt")
self.assertEqual(t[13], draft.title)
author = draft.documentauthor_set.order_by("order").get()
self.assertEqual(t[14], "%s <%s>" % (author.author.person.name, author.author.address))
self.assertEqual(t[15], "%s <%s>" % (draft.shepherd, draft.shepherd.email_address()))
self.assertEqual(t[16], "%s <%s>" % (draft.ad, draft.ad.email_address()))
# test RFC
draft.set_state(State.objects.get(type="draft", slug="rfc"))
DocAlias.objects.create(name="rfc1234", document=draft)
t = get_fields(all_id2_txt())
self.assertEqual(t[4], "1234")
# test Replaced
draft.set_state(State.objects.get(type="draft", slug="repl"))
RelatedDocument.objects.create(
relationship=DocRelationshipName.objects.get(slug="replaces"),
source=Document.objects.create(type_id="draft", rev="00", name="draft-test-replacement"),
target=draft.docalias_set.get(name__startswith="draft"))
t = get_fields(all_id2_txt())
self.assertEqual(t[5], "draft-test-replacement")
# test Last Call
draft.set_state(State.objects.get(type="draft", slug="active"))
draft.set_state(State.objects.get(type="draft-iesg", slug="lc"))
e = LastCallDocEvent.objects.create(doc=draft, type="sent_last_call", expires=datetime.datetime.now() + datetime.timedelta(days=14), by=draft.ad)
DocAlias.objects.create(name="rfc1234", document=draft)
t = get_fields(all_id2_txt())
self.assertEqual(t[11], e.expires.strftime("%Y-%m-%d"))
def test_id_index_txt(self):
draft = make_test_data()
draft.set_state(State.objects.get(type="draft", slug="active"))
txt = id_index_txt()
self.assertTrue(draft.name + "-" + draft.rev in txt)
self.assertTrue(draft.title in txt)
self.assertTrue(draft.abstract[:20] not in txt)
txt = id_index_txt(with_abstracts=True)
self.assertTrue(draft.abstract[:20] in txt)

View file

@ -1,20 +0,0 @@
301 /drafts/wgid/1041/
404 /drafts/wgid/987654/
301 /drafts/wg/idr/
301 /drafts/rfc/
301 /drafts/current/
301 /drafts/all/
301 /drafts/dead/
#301 /drafts/9574/related/
#301 /drafts/9574/
301 /drafts/draft-ietf-dnsext-dnssec-protocol/related/
301 /drafts/draft-ietf-dnsext-dnssec-protocol/
#404 /drafts/987654/
301 /drafts/all_id_txt.html
301 /drafts/all_id.html
301 /drafts/
#200,heavy /drafts/_test/all_id.txt
# this takes 3 minutes, so disabled by default
#200,heavy /drafts/_test/all_id2.txt
#200,heavy /drafts/_test/id_index.txt
#200,heavy /drafts/_test/id_abstracts.txt

View file

@ -1,27 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf import settings
from django.conf.urls.defaults import patterns
from ietf.idindex import views
urlpatterns = patterns('',
(r'^$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/'}),
(r'^all/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/all/'}),
(r'^rfc/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/all/#rfc'}),
(r'^dead/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/all/#dead'}),
(r'^current/$', 'django.views.generic.simple.redirect_to', { 'url': '/doc/active/'}),
(r'^(?P<object_id>\d+)/(related/)?$', views.redirect_id),
(r'^(?P<filename>[^/]+)/(related/)?$', views.redirect_filename),
(r'^wgid/(?P<id>\d+)/$', views.wgdocs_redirect_id),
(r'^wg/(?P<acronym>[^/]+)/$', views.wgdocs_redirect_acronym),
(r'^all_id(?:_txt)?.html$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/id/all_id.txt' }),
)
if settings.SERVER_MODE != 'production':
# these haven't been ported
urlpatterns += patterns('',
(r'^_test/all_id.txt$', views.test_all_id_txt),
(r'^_test/all_id2.txt$', views.test_all_id2_txt),
(r'^_test/id_index.txt$', views.test_id_index_txt),
(r'^_test/id_abstracts.txt$', views.test_id_abstracts_txt)
)

View file

@ -1,199 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved
# Portions Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# * Neither the name of the Nokia Corporation and/or its
# subsidiary(-ies) nor the names of its contributors may be used
# to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from django.http import HttpResponse, HttpResponsePermanentRedirect
from django.template import loader
from django.shortcuts import get_object_or_404
from django.conf import settings
from ietf.idtracker.models import Acronym, IETFWG, InternetDraft, IDInternal,PersonOrOrgInfo, Area
from ietf.idtracker.templatetags.ietf_filters import clean_whitespace
import re
import sys
from datetime import datetime as Datetime
import pytz
def all_id_txt():
# we need a distinct to prevent the queries below from multiplying the result
all_ids = InternetDraft.objects.order_by('name').exclude(name__startswith="rfc").distinct()
inactive_states = ["pub", "watching", "dead"]
in_track_ids = all_ids.exclude(states__type="draft", states__slug="rfc").filter(states__type="draft-iesg").exclude(states__type="draft-iesg", states__slug__in=inactive_states)
not_in_track = all_ids.filter(states__type="draft", states__slug="rfc") | all_ids.exclude(states__type="draft-iesg") | all_ids.filter(states__type="draft-iesg", states__slug__in=inactive_states)
active = not_in_track.filter(states__type="draft", states__slug="active")
published = not_in_track.filter(states__type="draft", states__slug="rfc")
expired = not_in_track.filter(states__type="draft", states__slug="expired")
withdrawn_submitter = not_in_track.filter(states__type="draft", states__slug="auth-rm")
withdrawn_ietf = not_in_track.filter(states__type="draft", states__slug="ietf-rm")
replaced = not_in_track.filter(states__type="draft", states__slug="repl")
return loader.render_to_string("idindex/all_ids.txt",
{ 'in_track_ids':in_track_ids,
'active':active,
'published':published,
'expired':expired,
'withdrawn_submitter':withdrawn_submitter,
'withdrawn_ietf':withdrawn_ietf,
'replaced':replaced})
def all_id2_entry(id):
fields = []
# 0
fields.append(id.filename+"-"+id.revision_display())
# 1
fields.append(-1) # this used to be id.id_document_tag, we don't have this identifier anymore
# 2
status = str(id.get_state())
fields.append(status)
# 3
iesgstate = id.idstate() if status=="Active" else ""
fields.append(iesgstate)
# 4
fields.append(id.rfc_number if status=="RFC" else "")
# 5
try:
fields.append(id.replaced_by.filename)
except (AttributeError, InternetDraft.DoesNotExist):
fields.append("")
# 6
fields.append(id.revision_date)
# 7
group_acronym = "" if id.group.type_id == "area" else id.group.acronym
if group_acronym == "none":
group_acronym = ""
fields.append(group_acronym)
# 8
area = ""
if id.group.type_id == "area":
area = id.group.acronym
elif id.group.type_id == "wg" and id.group.parent:
area = id.group.parent.acronym
fields.append(area)
# 9 responsible AD name
fields.append(id.idinternal.job_owner if id.idinternal else "")
# 10
s = id.intended_status
if s and str(s) not in ("None","Request"):
fields.append(str(s))
else:
fields.append("")
# 11
if (iesgstate=="In Last Call") or iesgstate.startswith("In Last Call::"):
fields.append(id.lc_expiration_date)
else:
fields.append("")
# 12
fields.append(id.file_type if status=="Active" else "")
# 13
fields.append(clean_whitespace(id.title))
# 14
authors = []
for author in sorted(id.authors.all(), key=lambda x: x.final_author_order()):
try:
realname = unicode(author.person)
email = author.email() or ""
name = re.sub(u"[<>@,]", u"", realname) + u" <"+re.sub(u"[<>,]", u"", email).strip()+u">"
authors.append(clean_whitespace(name))
except PersonOrOrgInfo.DoesNotExist:
pass
fields.append(u", ".join(authors))
# 15
if id.shepherd:
shepherd = id.shepherd
realname = unicode(shepherd)
email = shepherd.email_address()
name = re.sub(u"[<>@,]", u"", realname) + u" <"+re.sub(u"[<>,]", u"", email).strip()+u">"
else:
name = u""
fields.append(name)
# 16 Responsible AD name and email
if id.ad:
ad = id.ad
realname = unicode(ad)
email = ad.email_address()
name = re.sub(u"[<>@,]", u"", realname) + u" <"+re.sub(u"[<>,]", u"", email).strip()+u">"
else:
name = u""
fields.append(name)
#
return "\t".join([unicode(x) for x in fields])
def all_id2_txt():
all_ids = InternetDraft.objects.order_by('name').exclude(name__startswith="rfc").select_related('group', 'group__parent', 'ad')
data = "\n".join(all_id2_entry(id) for id in all_ids)
return loader.render_to_string("idindex/all_id2.txt",{'data':data})
def id_index_txt():
groups = IETFWG.objects.all()
return loader.render_to_string("idindex/id_index.txt", {'groups':groups})
def id_abstracts_txt():
groups = IETFWG.objects.all()
return loader.render_to_string("idindex/id_abstracts.txt", {'groups':groups, 'time':Datetime.now(pytz.UTC).strftime("%Y-%m-%d %H:%M:%S %Z")})
def test_all_id_txt(request):
return HttpResponse(all_id_txt(), mimetype='text/plain')
def test_all_id2_txt(request):
return HttpResponse(all_id2_txt(), mimetype='text/plain')
def test_id_index_txt(request):
return HttpResponse(id_index_txt(), mimetype='text/plain')
def test_id_abstracts_txt(request):
return HttpResponse(id_abstracts_txt(), mimetype='text/plain')
def redirect_id(request, object_id):
'''Redirect from historical document ID to preferred filename url.'''
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return HttpResponsePermanentRedirect("/doc/")
doc = get_object_or_404(InternetDraft, id_document_tag=object_id)
return HttpResponsePermanentRedirect("/doc/"+doc.filename+"/")
def redirect_filename(request, filename):
return HttpResponsePermanentRedirect("/doc/"+filename+"/")
def wgdocs_redirect_id(request, id):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.group.models import Group
group = get_object_or_404(Group, id=id)
return HttpResponsePermanentRedirect("/wg/%s/" % group.acronym)
group = get_object_or_404(Acronym, acronym_id=id)
return HttpResponsePermanentRedirect("/wg/"+group.acronym+"/")
def wgdocs_redirect_acronym(request, acronym):
return HttpResponsePermanentRedirect("/wg/"+acronym+"/")

View file

@ -6,105 +6,64 @@ from django.db.models import Q
import datetime, os, shutil, glob, re, itertools
from ietf.idtracker.models import InternetDraft, IDDates, IDStatus, IDState, DocumentComment, IDAuthor, WGChair
from ietf.utils.mail import send_mail, send_mail_subj
from ietf.idrfc.utils import log_state_changed, add_document_comment
from ietf.doc.models import Document, DocEvent, save_document_in_history, State
from ietf.name.models import DocTagName
from ietf.idrfc.utils import log_state_changed
from ietf.doc.models import Document, DocEvent, State, save_document_in_history, IESG_SUBSTATE_TAGS
from ietf.person.models import Person, Email
from ietf.meeting.models import Meeting
def in_id_expire_freeze(when=None):
if when == None:
when = datetime.datetime.now()
def expirable_draft(draft):
"""Return whether draft is in an expirable state or not. This is
the single draft version of the logic in expirable_drafts. These
two functions need to be kept in sync."""
return (draft.expires and draft.get_state_slug() == "active"
and draft.get_state_slug("draft-iesg") in (None, "watching", "dead")
and draft.get_state_slug("draft-stream-%s" % draft.stream_id) not in ("rfc-edit", "pub")
and not draft.tags.filter(slug="rfc-rev"))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
d = Meeting.get_second_cut_off()
else:
d = IDDates.objects.get(id=IDDates.SECOND_CUT_OFF).date
# for some reason, the old Perl code started at 9 am
second_cut_off = datetime.datetime.combine(d, datetime.time(9, 0))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
d = Meeting.get_ietf_monday()
else:
d = IDDates.objects.get(id=IDDates.IETF_MONDAY).date
ietf_monday = datetime.datetime.combine(d, datetime.time(0, 0))
return second_cut_off <= when < ietf_monday
def expirable_documents():
def expirable_drafts():
"""Return a queryset with expirable drafts."""
# the general rule is that each active draft is expirable, unless
# it's in a state where we shouldn't touch it
d = Document.objects.filter(states__type="draft", states__slug="active").exclude(tags="rfc-rev")
d = Document.objects.filter(states__type="draft", states__slug="active").exclude(expires=None)
nonexpirable_states = []
# all IESG states except AD Watching and Dead block expiry
nonexpirable_states += list(State.objects.filter(used=True, type="draft-iesg").exclude(slug__in=("watching", "dead")))
# Sent to RFC Editor and RFC Published block expiry (the latter
# sent to RFC Editor and RFC Published block expiry (the latter
# shouldn't be possible for an active draft, though)
nonexpirable_states += list(State.objects.filter(used=True, type__in=("draft-stream-iab", "draft-stream-irtf", "draft-stream-ise"), slug__in=("rfc-edit", "pub")))
return d.exclude(states__in=nonexpirable_states).distinct()
d = d.exclude(states__in=nonexpirable_states)
def get_soon_to_expire_ids(days):
start_date = datetime.date.today() - datetime.timedelta(InternetDraft.DAYS_TO_EXPIRE - 1)
end_date = start_date + datetime.timedelta(days - 1)
for d in InternetDraft.objects.filter(revision_date__gte=start_date,revision_date__lte=end_date,status__status='Active'):
if d.can_expire():
yield d
# under review by the RFC Editor blocks expiry
d = d.exclude(tags="rfc-rev")
def get_soon_to_expire_idsREDESIGN(days):
return d.distinct()
def get_soon_to_expire_drafts(days_of_warning):
start_date = datetime.date.today() - datetime.timedelta(1)
end_date = start_date + datetime.timedelta(days - 1)
end_date = start_date + datetime.timedelta(days_of_warning)
return expirable_drafts().filter(expires__gte=start_date, expires__lt=end_date)
def get_expired_drafts():
return expirable_drafts().filter(expires__lt=datetime.date.today() + datetime.timedelta(1))
def in_draft_expire_freeze(when=None):
if when == None:
when = datetime.datetime.now()
d = Meeting.get_second_cut_off()
# for some reason, the old Perl code started at 9 am
second_cut_off = datetime.datetime.combine(d, datetime.time(9, 0))
for d in expirable_documents():
if d.expires and start_date <= d.expires.date() <= end_date:
yield d
def get_expired_ids():
cut_off = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE)
return InternetDraft.objects.filter(
revision_date__lte=cut_off,
status__status="Active",
review_by_rfc_editor=0).filter(
Q(idinternal=None) | Q(idinternal__cur_state__document_state_id__gte=42))
def get_expired_idsREDESIGN():
today = datetime.date.today()
for d in expirable_documents():
if d.expires and d.expires.date() <= today:
yield d
def send_expire_warning_for_id(doc):
expiration = doc.expiration()
# Todo:
#second_cutoff = IDDates.objects.get(date_id=2)
#ietf_monday = IDDates.objects.get(date_id=3)
#freeze_delta = ietf_monday - second_cutoff
# # The I-D expiration job doesn't run while submissions are frozen.
# if ietf_monday > expiration > second_cutoff:
# expiration += freeze_delta
d = Meeting.get_ietf_monday()
ietf_monday = datetime.datetime.combine(d, datetime.time(0, 0))
authors = doc.authors.all()
to_addrs = [author.email() for author in authors if author.email()]
cc_addrs = None
if doc.group.acronym != 'none':
cc_addrs = [chair.person.email() for chair in WGChair.objects.filter(group_acronym=doc.group)]
return second_cut_off <= when < ietf_monday
if to_addrs or cc_addrs:
send_mail_subj(None, to_addrs, None, 'notify_expirations/subject.txt', 'notify_expirations/body.txt',
{
'draft':doc,
'expiration':expiration,
},
cc_addrs)
def send_expire_warning_for_idREDESIGN(doc):
def send_expire_warning_for_draft(doc):
if doc.get_state_slug("draft-iesg") == "dead":
return # don't warn about dead documents
@ -130,23 +89,7 @@ def send_expire_warning_for_idREDESIGN(doc):
),
cc=cc)
def send_expire_notice_for_id(doc):
doc.dunn_sent_date = datetime.date.today()
doc.save()
if not doc.idinternal:
return
request = None
to = u"%s <%s>" % doc.idinternal.job_owner.person.email()
send_mail(request, to,
"I-D Expiring System <ietf-secretariat-reply@ietf.org>",
u"I-D was expired %s" % doc.file_tag(),
"idrfc/id_expired_email.txt",
dict(doc=doc,
state=doc.idstate()))
def send_expire_notice_for_idREDESIGN(doc):
def send_expire_notice_for_draft(doc):
if not doc.ad or doc.get_state_slug("draft-iesg") == "dead":
return
@ -163,42 +106,6 @@ def send_expire_notice_for_idREDESIGN(doc):
state=state,
))
def expire_id(doc):
def move_file(f):
src = os.path.join(settings.INTERNET_DRAFT_PATH, f)
dst = os.path.join(settings.INTERNET_DRAFT_ARCHIVE_DIR, f)
if os.path.exists(src):
shutil.move(src, dst)
move_file("%s-%s.txt" % (doc.filename, doc.revision_display()))
move_file("%s-%s.txt.p7s" % (doc.filename, doc.revision_display()))
move_file("%s-%s.ps" % (doc.filename, doc.revision_display()))
move_file("%s-%s.pdf" % (doc.filename, doc.revision_display()))
new_revision = "%02d" % (int(doc.revision) + 1)
new_file = open(os.path.join(settings.INTERNET_DRAFT_PATH, "%s-%s.txt" % (doc.filename, new_revision)), 'w')
txt = render_to_string("idrfc/expire_text.txt",
dict(doc=doc,
authors=[a.person.email() for a in doc.authors.all()],
expire_days=InternetDraft.DAYS_TO_EXPIRE))
new_file.write(txt)
new_file.close()
doc.revision = new_revision
doc.expiration_date = datetime.date.today()
doc.last_modified_date = datetime.date.today()
doc.status = IDStatus.objects.get(status="Expired")
doc.save()
if doc.idinternal:
if doc.idinternal.cur_state_id != IDState.DEAD:
doc.idinternal.change_state(IDState.objects.get(document_state_id=IDState.DEAD), None)
log_state_changed(None, doc, "system")
add_document_comment(None, doc, "Document is expired by system")
def move_draft_files_to_archive(doc, rev):
def move_file(f):
src = os.path.join(settings.INTERNET_DRAFT_PATH, f)
@ -211,7 +118,7 @@ def move_draft_files_to_archive(doc, rev):
for t in file_types:
move_file("%s-%s.%s" % (doc.name, rev, t))
def expire_idREDESIGN(doc):
def expire_draft(doc):
# clean up files
move_draft_files_to_archive(doc, doc.rev)
@ -222,7 +129,7 @@ def expire_idREDESIGN(doc):
if doc.latest_event(type='started_iesg_process'):
dead_state = State.objects.get(used=True, type="draft-iesg", slug="dead")
prev = doc.get_state("draft-iesg")
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
prev_tag = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
prev_tag = prev_tag[0] if prev_tag else None
if prev != dead_state:
doc.set_state(dead_state)
@ -239,67 +146,7 @@ def expire_idREDESIGN(doc):
doc.time = datetime.datetime.now()
doc.save()
def clean_up_id_files():
"""Move unidentified and old files out of the Internet Draft directory."""
cut_off = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE)
pattern = os.path.join(settings.INTERNET_DRAFT_PATH, "draft-*.*")
files = []
filename_re = re.compile('^(.*)-(\d\d)$')
def splitext(fn):
"""
Split the pathname path into a pair (root, ext) such that root + ext
== path, and ext is empty or begins with a period and contains all
periods in the last path component.
This differs from os.path.splitext in the number of periods in the ext
parts when the final path component containt more than one period.
"""
s = fn.rfind("/")
if s == -1:
s = 0
i = fn[s:].find(".")
if i == -1:
return fn, ''
else:
return fn[:s+i], fn[s+i:]
for path in glob.glob(pattern):
basename = os.path.basename(path)
stem, ext = splitext(basename)
match = filename_re.search(stem)
if not match:
filename, revision = ("UNKNOWN", "00")
else:
filename, revision = match.groups()
def move_file_to(subdir):
shutil.move(path,
os.path.join(settings.INTERNET_DRAFT_ARCHIVE_DIR, subdir, basename))
try:
doc = InternetDraft.objects.get(filename=filename, revision=revision)
if doc.status_id == 3: # RFC
if ext != ".txt":
move_file_to("unknown_ids")
elif doc.status_id in (2, 4, 5, 6) and doc.expiration_date and doc.expiration_date < cut_off:
# Expired, Withdrawn by Auth, Replaced, Withdrawn by IETF,
# and expired more than DAYS_TO_EXPIRE ago
if os.path.getsize(path) < 1500:
move_file_to("deleted_tombstones")
# revert version after having deleted tombstone
doc.revision = "%02d" % (int(revision) - 1)
doc.expired_tombstone = True
doc.save()
else:
move_file_to("expired_without_tombstone")
except InternetDraft.DoesNotExist:
move_file_to("unknown_ids")
def clean_up_id_filesREDESIGN():
def clean_up_draft_files():
"""Move unidentified and old files out of the Internet Draft directory."""
cut_off = datetime.date.today()
@ -314,7 +161,7 @@ def clean_up_id_filesREDESIGN():
periods in the last path component.
This differs from os.path.splitext in the number of periods in the ext
parts when the final path component containt more than one period.
parts when the final path component contains more than one period.
"""
s = fn.rfind("/")
if s == -1:
@ -357,11 +204,3 @@ def clean_up_id_filesREDESIGN():
except Document.DoesNotExist:
move_file_to("unknown_ids")
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
get_soon_to_expire_ids = get_soon_to_expire_idsREDESIGN
get_expired_ids = get_expired_idsREDESIGN
send_expire_warning_for_id = send_expire_warning_for_idREDESIGN
send_expire_notice_for_id = send_expire_notice_for_idREDESIGN
expire_id = expire_idREDESIGN
clean_up_id_files = clean_up_id_filesREDESIGN

View file

@ -8,7 +8,7 @@ from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo
from ietf.idrfc.mails import *
from ietf.idrfc.utils import *
from ietf.doc.models import Document, DocEvent, LastCallDocEvent, WriteupDocEvent, save_document_in_history, State
from ietf.doc.models import *
from ietf.person.models import Person
def request_last_call(request, doc):
@ -83,7 +83,7 @@ def expire_last_callREDESIGN(doc):
prev = doc.get_state("draft-iesg")
doc.set_state(state)
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
prev_tag = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
prev_tag = prev_tag[0] if prev_tag else None
if prev_tag:
doc.tags.remove(prev_tag)

View file

@ -30,42 +30,31 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import datetime
from django import template
from django.core.urlresolvers import reverse as urlreverse
from django.conf import settings
from django.db.models import Q
from ietf.idtracker.models import IDInternal, BallotInfo
from ietf.idrfc.idrfc_wrapper import position_to_string, BALLOT_ACTIVE_STATES
from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days
from ietf.ietfauth.decorators import has_role
from ietf.doc.models import BallotDocEvent, BallotPositionDocEvent
from django.utils.safestring import mark_safe
from datetime import date
from ietf.ietfauth.utils import user_is_person, has_role
from ietf.doc.models import BallotDocEvent, BallotPositionDocEvent, IESG_BALLOT_ACTIVE_STATES, IESG_SUBSTATE_TAGS
register = template.Library()
def get_user_name(context):
if 'user' in context and context['user'].is_authenticated():
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.person.models import Person
try:
return context['user'].get_profile().plain_name()
except Person.DoesNotExist:
return None
person = context['user'].get_profile().person()
if person:
return str(person)
return None
def render_ballot_icon(user, doc):
if not doc:
return ""
# FIXME: temporary backwards-compatibility hack
from ietf.doc.models import Document
if not isinstance(doc, Document):
doc = doc._draft
if doc.type_id == "draft":
s = doc.get_state("draft-iesg")
if s and s.name not in BALLOT_ACTIVE_STATES:
if doc.get_state_slug("draft-iesg") not in IESG_BALLOT_ACTIVE_STATES:
return ""
elif doc.type_id == "charter":
if doc.get_state_slug() not in ("intrev", "iesgrev"):
@ -77,12 +66,10 @@ def render_ballot_icon(user, doc):
if doc.get_state_slug() not in ("iesgeval","defer"):
return ""
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
ballot = doc.active_ballot()
if not ballot:
return ""
edit_position_url = urlreverse('ietf.idrfc.views_ballot.edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
def sort_key(t):
_, pos = t
if not pos:
@ -95,11 +82,18 @@ def render_ballot_icon(user, doc):
positions = list(doc.active_ballot().active_ad_positions().items())
positions.sort(key=sort_key)
cm = ""
edit_position_url = ""
if has_role(user, "Area Director"):
cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
edit_position_url = urlreverse('ietf.idrfc.views_ballot.edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
res = ['<table class="ballot_icon" title="IESG Evaluation Record (click to show more, right-click to edit position)" onclick="showBallot(\'' + doc.name + '\',\'' + str(edit_position_url) + '\')"' + cm + '>']
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)),
urlreverse("ietf.doc.views_doc.ballot_popup", kwargs=dict(name=doc.name, ballot_id=ballot.pk)),
edit_position_url,
title
)]
res.append("<tr>")
@ -110,13 +104,13 @@ def render_ballot_icon(user, doc):
c = "position-%s" % (pos.pos.slug if pos else "norecord")
if hasattr(user, "get_profile") and ad == user.get_profile():
if user_is_person(user, ad):
c += " my"
res.append('<td class="%s" />' % c)
res.append("</tr>")
res.append("</table>")
res.append("</table></a>")
return "".join(res)
@ -125,48 +119,51 @@ class BallotIconNode(template.Node):
self.doc_var = doc_var
def render(self, context):
doc = template.resolve_variable(self.doc_var, context)
#if hasattr(doc, "_idinternal"):
# # hack for old schema
# doc = doc._idinternal
return render_ballot_icon(context.get("user"), doc)
def do_ballot_icon(parser, token):
try:
tagName, docName = token.split_contents()
tag_name, doc_name = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0]
return BallotIconNode(docName)
return BallotIconNode(doc_name)
register.tag('ballot_icon', do_ballot_icon)
@register.filter
def my_position(doc, user):
user_name = get_user_name({'user':user})
if not user_name:
return None
if not in_group(user, "Area_Director"):
if not has_role(user, "Area Director"):
return None
# FIXME: temporary backwards-compatibility hack
from ietf.doc.models import Document
if not isinstance(doc, Document):
doc = doc._draft
ballot = doc.active_ballot()
pos = "No Record"
if ballot:
changed_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad__name=user_name, ballot=ballot)
if changed_pos:
pos = changed_pos.pos.name;
changed_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad__user=user, ballot=ballot)
if changed_pos:
pos = changed_pos.pos.name;
return pos
@register.filter
@register.filter()
def state_age_colored(doc):
if doc.type.slug == 'draft':
if not doc.latest_event(type='started_iesg_process'):
return ""
# FIXME: temporary backwards-compatibility hack
from ietf.doc.models import Document
if not isinstance(doc, Document):
doc = doc._draft
if doc.type_id == 'draft':
if not doc.get_state_slug() in ["active", "rfc"]:
# Don't show anything for expired/withdrawn/replaced drafts
return ""
main_state = doc.get_state('draft-iesg')
IESG_SUBSTATE_TAGS = ('point', 'ad-f-up', 'need-rev', 'extpty')
sub_states = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
main_state = doc.get_state_slug('draft-iesg')
if not main_state:
return ""
if main_state.slug in ["dead","watching","pub"]:
if main_state in ["dead", "watching", "pub"]:
return ""
try:
state_date = doc.docevent_set.filter(
@ -181,26 +178,26 @@ def state_age_colored(doc):
Q(desc__istartswith="IESG process started in state")
).order_by('-time')[0].time.date()
except IndexError:
state_date = date(1990,1,1)
days = timesince_days(state_date)
state_date = datetime.date(1990,1,1)
days = (datetime.date.today() - state_date).days
# loosely based on
# http://trac.tools.ietf.org/group/iesg/trac/wiki/PublishPath
if main_state.slug == "lc":
if main_state == "lc":
goal1 = 30
goal2 = 30
elif main_state.slug == "rfcqueue":
elif main_state == "rfcqueue":
goal1 = 60
goal2 = 120
elif main_state.slug in ["lc-req", "ann"]:
elif main_state in ["lc-req", "ann"]:
goal1 = 4
goal2 = 7
elif 'need-rev' in [x.slug for x in sub_states]:
elif 'need-rev' in [x.slug for x in doc.tags.all()]:
goal1 = 14
goal2 = 28
elif main_state.slug == "pub-req":
elif main_state == "pub-req":
goal1 = 7
goal2 = 14
elif main_state.slug == "ad-eval":
elif main_state == "ad-eval":
goal1 = 14
goal2 = 28
else:
@ -216,6 +213,7 @@ def state_age_colored(doc):
title = ' title="Goal is &lt;%d days"' % (goal1,)
else:
title = ''
return '<span class="%s"%s>(for&nbsp;%d&nbsp;day%s)</span>' % (class_name,title,days,('','s')[days != 1])
return mark_safe('<span class="%s"%s>(for&nbsp;%d&nbsp;day%s)</span>' % (
class_name, title, days, 's' if days != 1 else ''))
else:
return ""

View file

@ -1013,13 +1013,13 @@ class ExpireIDsTestCase(django.test.TestCase):
shutil.rmtree(self.id_dir)
shutil.rmtree(self.archive_dir)
def write_id_file(self, name, size):
def write_draft_file(self, name, size):
f = open(os.path.join(self.id_dir, name), 'w')
f.write("a" * size)
f.close()
def test_in_id_expire_freeze(self):
from ietf.idrfc.expire import in_id_expire_freeze
def test_in_draft_expire_freeze(self):
from ietf.idrfc.expire import in_draft_expire_freeze
Meeting.objects.create(number="123",
type=MeetingTypeName.objects.get(slug="ietf"),
@ -1027,70 +1027,70 @@ class ExpireIDsTestCase(django.test.TestCase):
second_cut_off = Meeting.get_second_cut_off()
ietf_monday = Meeting.get_ietf_monday()
self.assertTrue(not in_id_expire_freeze(datetime.datetime.combine(second_cut_off - datetime.timedelta(days=7), time(0, 0, 0))))
self.assertTrue(not in_id_expire_freeze(datetime.datetime.combine(second_cut_off, time(0, 0, 0))))
self.assertTrue(in_id_expire_freeze(datetime.datetime.combine(second_cut_off + datetime.timedelta(days=7), time(0, 0, 0))))
self.assertTrue(in_id_expire_freeze(datetime.datetime.combine(ietf_monday - datetime.timedelta(days=1), time(0, 0, 0))))
self.assertTrue(not in_id_expire_freeze(datetime.datetime.combine(ietf_monday, time(0, 0, 0))))
self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(second_cut_off - datetime.timedelta(days=7), time(0, 0, 0))))
self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(second_cut_off, time(0, 0, 0))))
self.assertTrue(in_draft_expire_freeze(datetime.datetime.combine(second_cut_off + datetime.timedelta(days=7), time(0, 0, 0))))
self.assertTrue(in_draft_expire_freeze(datetime.datetime.combine(ietf_monday - datetime.timedelta(days=1), time(0, 0, 0))))
self.assertTrue(not in_draft_expire_freeze(datetime.datetime.combine(ietf_monday, time(0, 0, 0))))
def test_warn_expirable_ids(self):
from ietf.idrfc.expire import get_soon_to_expire_ids, send_expire_warning_for_id
def test_warn_expirable_drafts(self):
from ietf.idrfc.expire import get_soon_to_expire_drafts, send_expire_warning_for_draft
draft = make_test_data()
self.assertEquals(len(list(get_soon_to_expire_ids(14))), 0)
self.assertEquals(len(list(get_soon_to_expire_drafts(14))), 0)
# hack into expirable state
draft.unset_state("draft-iesg")
draft.expires = datetime.datetime.now() + datetime.timedelta(days=10)
draft.save()
self.assertEquals(len(list(get_soon_to_expire_ids(14))), 1)
self.assertEquals(len(list(get_soon_to_expire_drafts(14))), 1)
# test send warning
mailbox_before = len(outbox)
send_expire_warning_for_id(draft)
send_expire_warning_for_draft(draft)
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertTrue("aread@ietf.org" in str(outbox[-1])) # author
self.assertTrue("wgchairman@ietf.org" in str(outbox[-1]))
def test_expire_ids(self):
from ietf.idrfc.expire import get_expired_ids, send_expire_notice_for_id, expire_id
def test_expire_drafts(self):
from ietf.idrfc.expire import get_expired_drafts, send_expire_notice_for_draft, expire_draft
draft = make_test_data()
self.assertEquals(len(list(get_expired_ids())), 0)
self.assertEquals(len(list(get_expired_drafts())), 0)
# hack into expirable state
draft.unset_state("draft-iesg")
draft.expires = datetime.datetime.now()
draft.save()
self.assertEquals(len(list(get_expired_ids())), 1)
self.assertEquals(len(list(get_expired_drafts())), 1)
draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="watching"))
self.assertEquals(len(list(get_expired_ids())), 1)
self.assertEquals(len(list(get_expired_drafts())), 1)
draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva"))
self.assertEquals(len(list(get_expired_ids())), 0)
self.assertEquals(len(list(get_expired_drafts())), 0)
# test notice
mailbox_before = len(outbox)
send_expire_notice_for_id(draft)
send_expire_notice_for_draft(draft)
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertTrue("expired" in outbox[-1]["Subject"])
# test expiry
txt = "%s-%s.txt" % (draft.name, draft.rev)
self.write_id_file(txt, 5000)
self.write_draft_file(txt, 5000)
expire_id(draft)
expire_draft(draft)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.get_state_slug(), "expired")
@ -1099,16 +1099,16 @@ class ExpireIDsTestCase(django.test.TestCase):
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt)))
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, txt)))
def test_clean_up_id_files(self):
def test_clean_up_draft_files(self):
draft = make_test_data()
from ietf.idrfc.expire import clean_up_id_files
from ietf.idrfc.expire import clean_up_draft_files
# put unknown file
unknown = "draft-i-am-unknown-01.txt"
self.write_id_file(unknown, 5000)
self.write_draft_file(unknown, 5000)
clean_up_id_files()
clean_up_draft_files()
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, unknown)))
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "unknown_ids", unknown)))
@ -1116,9 +1116,9 @@ class ExpireIDsTestCase(django.test.TestCase):
# put file with malformed name (no revision)
malformed = draft.name + ".txt"
self.write_id_file(malformed, 5000)
self.write_draft_file(malformed, 5000)
clean_up_id_files()
clean_up_draft_files()
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, malformed)))
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "unknown_ids", malformed)))
@ -1129,11 +1129,11 @@ class ExpireIDsTestCase(django.test.TestCase):
draft.save()
txt = "%s-%s.txt" % (draft.name, draft.rev)
self.write_id_file(txt, 5000)
self.write_draft_file(txt, 5000)
pdf = "%s-%s.pdf" % (draft.name, draft.rev)
self.write_id_file(pdf, 5000)
self.write_draft_file(pdf, 5000)
clean_up_id_files()
clean_up_draft_files()
# txt files shouldn't be moved (for some reason)
self.assertTrue(os.path.exists(os.path.join(self.id_dir, txt)))
@ -1157,9 +1157,9 @@ class ExpireIDsTestCase(django.test.TestCase):
# expired without tombstone
txt = "%s-%s.txt" % (draft.name, draft.rev)
self.write_id_file(txt, 5000)
self.write_draft_file(txt, 5000)
clean_up_id_files()
clean_up_draft_files()
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt)))
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "expired_without_tombstone", txt)))
@ -1169,9 +1169,9 @@ class ExpireIDsTestCase(django.test.TestCase):
revision_before = draft.rev
txt = "%s-%s.txt" % (draft.name, draft.rev)
self.write_id_file(txt, 1000) # < 1500 means tombstone
self.write_draft_file(txt, 1000) # < 1500 means tombstone
clean_up_id_files()
clean_up_draft_files()
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt)))
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "deleted_tombstones", txt)))

View file

@ -37,15 +37,20 @@ from ietf.doc import views_status_change
from ietf.doc import views_doc
urlpatterns = patterns('',
(r'^/?$', views_search.search_main),
(r'^search/$', views_search.search_results),
(r'^all/$', views_search.all),
(r'^active/$', views_search.active),
(r'^in-last-call/$', views_search.in_last_call),
url(r'^rfc-status-changes/$', views_status_change.rfc_status_changes, name='rfc_status_changes'),
url(r'^start-rfc-status-change/(?P<name>[A-Za-z0-9._+-]*)$', views_status_change.start_rfc_status_change, name='start_rfc_status_change'),
url(r'^ad/(?P<name>[A-Za-z0-9.-]+)/$', views_search.by_ad, name="doc_search_by_ad"),
url(r'^ad2/(?P<name>[A-Za-z0-9.-]+)/$', views_search.by_ad2, name="doc_search_by_ad2"),
(r'^/?$', views_search.search),
url(r'^search/$', views_search.search, name="doc_search"),
url(r'^in-last-call/$', views_search.drafts_in_last_call, name="drafts_in_last_call"),
url(r'^ad/(?P<name>[A-Za-z0-9.-]+)/$', views_search.drafts_for_ad, name="drafts_for_ad"),
# (r'^all/$', views_search.all), # XXX CHECK MERGE
# (r'^active/$', views_search.active), # XXX CHECK MERGE
url(r'^rfc-status-changes/$', views_status_change.rfc_status_changes, name='rfc_status_changes'),
url(r'^start-rfc-status-change/(?P<name>[A-Za-z0-9._+-]*)$', views_status_change.start_rfc_status_change, name='start_rfc_status_change'),
url(r'^iesg/(?P<last_call_only>[A-Za-z0-9.-]+/)?$', views_search.drafts_in_iesg_process, name="drafts_in_iesg_process"),
# url(r'^ad2/(?P<name>[A-Za-z0-9.-]+)/$', views_search.by_ad2, name="doc_search_by_ad2"),
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'^(?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"),
@ -56,7 +61,7 @@ urlpatterns = patterns('',
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/$', views_doc.document_ballot, name="doc_ballot"),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/$', views_doc.document_ballot, name="doc_ballot"),
(r'^(?P<name>[A-Za-z0-9._+-]+)/doc.json$', views_doc.document_json),
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballotpopup/$', views_doc.ballot_for_popup),
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballotpopup/(?P<ballot_id>[0-9]+)/$', views_doc.ballot_popup),
#(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot.json$', views_doc.ballot_json), # legacy view
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/state/$', views_edit.change_state, name='doc_change_state'), # IESG state
@ -86,6 +91,8 @@ urlpatterns = patterns('',
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='doc_approve_ballot'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'),
url(r'^help/state/(?P<type>[\w-]+)/$', 'ietf.doc.views_help.state_help', name="state_help"),
(r'^(?P<name>charter-[A-Za-z0-9._+-]+)/', include('ietf.wgcharter.urls')),
(r'^(?P<name>[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')),
(r'^(?P<name>[A-Za-z0-9._+-]+)/status-change/', include('ietf.doc.urls_status_change')),
@ -97,4 +104,3 @@ urlpatterns += patterns('django.views.generic.simple',
url(r'^help/state/status-change/$', 'direct_to_template', { 'template': 'doc/states.html', 'extra_context': { 'states': State.objects.filter(type="statchg").order_by("order"),'title':"RFC Status Change" } }, name='help_status_change_states'),
)

View file

@ -64,12 +64,12 @@ def log_state_changed(request, doc, by, email_watch_list=True, note=''):
return change
def log_state_changedREDESIGN(request, doc, by, prev_iesg_state, prev_iesg_tag):
from ietf.doc.models import DocEvent
from ietf.doc.models import DocEvent, IESG_SUBSTATE_TAGS
state = doc.get_state("draft-iesg")
state_name = state.name
tags = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
tags = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
if tags:
state_name += "::" + tags[0].name

View file

@ -453,7 +453,7 @@ def defer_ballotREDESIGN(request, name):
prev_state = doc.friendly_state()
if doc.type_id == 'draft':
doc.set_state(State.objects.get(used=True, type="draft-iesg", slug='defer'))
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
prev_tag = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
prev_tag = prev_tag[0] if prev_tag else None
if prev_tag:
doc.tags.remove(prev_tag)
@ -641,7 +641,7 @@ def lastcalltextREDESIGN(request, name):
prev = doc.get_state("draft-iesg")
doc.set_state(State.objects.get(used=True, type="draft-iesg", slug='lc-req'))
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
prev_tag = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
prev_tag = prev_tag[0] if prev_tag else None
if prev_tag:
doc.tags.remove(prev_tag)
@ -1035,7 +1035,7 @@ def approve_ballotREDESIGN(request, name):
prev = doc.get_state("draft-iesg")
doc.set_state(new_state)
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
prev_tag = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
prev_tag = prev_tag[0] if prev_tag else None
if prev_tag:
doc.tags.remove(prev_tag)

View file

@ -47,8 +47,6 @@ class ChangeStateForm(forms.Form):
def change_state(request, name):
pass
IESG_SUBSTATE_TAGS = ('point', 'ad-f-up', 'need-rev', 'extpty')
class ChangeStateFormREDESIGN(forms.Form):
state = forms.ModelChoiceField(State.objects.filter(used=True, type="draft-iesg"), empty_label=None, required=True)
substate = forms.ModelChoiceField(DocTagName.objects.filter(slug__in=IESG_SUBSTATE_TAGS), required=False)
@ -93,7 +91,7 @@ def change_stateREDESIGN(request, name):
# tag handling is a bit awkward since the UI still works
# as if IESG tags are a substate
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
prev_tag = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
prev_tag = prev_tag[0] if prev_tag else None
if next_state != prev_state or tag != prev_tag:
@ -144,7 +142,7 @@ def change_stateREDESIGN(request, name):
else:
state = doc.get_state("draft-iesg")
t = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
t = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
form = ChangeStateForm(initial=dict(state=state.pk if state else None,
substate=t[0].pk if t else None))
form.docname=name

View file

@ -31,29 +31,26 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import re, datetime
from django import forms
from django.shortcuts import render_to_response
from django.db.models import Q
from django.template import RequestContext
from django.views.decorators.cache import cache_page
from ietf.idtracker.models import IDState, IESGLogin, IDSubState, Area, InternetDraft, Rfc, IDInternal, IETFWG
from ietf.idrfc.models import RfcIndex
from ietf.ipr.models import IprDraft
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponsePermanentRedirect
from ietf.idrfc.idrfc_wrapper import IdWrapper,RfcWrapper,IdRfcWrapper
from ietf.utils import normalize_draftname
from django.conf import settings
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
from ietf.idrfc.expire import expirable_draft
from ietf.utils import normalize_draftname
from ietf.doc.models import *
from ietf.person.models import *
from ietf.group.models import *
from ietf.ipr.models import IprDocAlias
from ietf.idindex.index import active_drafts_index_by_group
class SearchForm(forms.Form):
name = forms.CharField(required=False)
rfcs = forms.BooleanField(required=False,initial=True)
activeDrafts = forms.BooleanField(required=False,initial=True)
oldDrafts = forms.BooleanField(required=False,initial=False)
lucky = forms.BooleanField(required=False,initial=False)
rfcs = forms.BooleanField(required=False, initial=True)
activedrafts = forms.BooleanField(required=False, initial=True)
olddrafts = forms.BooleanField(required=False, initial=False)
by = forms.ChoiceField(choices=[(x,x) for x in ('author','group','area','ad','state')], required=False, initial='wg', label='Foobar')
author = forms.CharField(required=False)
@ -61,7 +58,9 @@ class SearchForm(forms.Form):
area = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), empty_label="any area", required=False)
ad = forms.ChoiceField(choices=(), required=False)
state = forms.ModelChoiceField(State.objects.filter(type="draft-iesg"), empty_label="any state", required=False)
subState = forms.ChoiceField(choices=(), required=False)
substate = forms.ChoiceField(choices=(), required=False)
sort = forms.ChoiceField(choices=(("document", "Document"), ("title", "Title"), ("date", "Date"), ("status", "Status"), ("ipr", "Ipr"), ("ad", "AD")), required=False, widget=forms.HiddenInput)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
@ -78,54 +77,138 @@ class SearchForm(forms.Form):
inactive_ads.sort(key=extract_last_name)
self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.plain_name()) for ad in active_ads] + [('', '------------------')] + [(ad.pk, ad.name) for ad in inactive_ads]
self.fields['subState'].choices = [('', 'any substate'), ('0', 'no substate')] + [(n.slug, n.name) for n in DocTagName.objects.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))]
self.fields['substate'].choices = [('', 'any substate'), ('0', 'no substate')] + [(n.slug, n.name) for n in DocTagName.objects.filter(slug__in=IESG_SUBSTATE_TAGS)]
def clean_name(self):
value = self.cleaned_data.get('name','')
return normalize_draftname(value)
def clean(self):
q = self.cleaned_data
# Reset query['by'] if needed
if 'by' not in q:
q['by'] = None
else:
for k in ('author','group','area','ad'):
if (q['by'] == k) and (k not in q or not q[k]):
if 'by' in q:
for k in ('author', 'group', 'area', 'ad'):
if q['by'] == k and not q.get(k):
q['by'] = None
if (q['by'] == 'state') and (not 'state' in q or not 'subState' in q or not (q['state'] or q['subState'])):
if q['by'] == 'state' and not (q.get("state") or q.get('substate')):
q['by'] = None
# Reset other fields
for k in ('author','group','area','ad'):
if q['by'] != k:
self.data[k] = ""
for k in ('author','group', 'area', 'ad'):
if k != q['by']:
q[k] = ""
if q['by'] != 'state':
self.data['state'] = ""
self.data['subState'] = ""
q['state'] = ""
q['subState'] = ""
q['state'] = q['substate'] = None
return q
def search_query(query_original, sort_by=None):
query = dict(query_original.items())
drafts = query['activeDrafts'] or query['oldDrafts']
if (not drafts) and (not query['rfcs']):
def wrap_value(v):
return lambda: v
def fill_in_search_attributes(docs):
# fill in some attributes for the search results to save some
# hairy template code and avoid repeated SQL queries - remaining
# queries we don't handle here are mostly implicit many-to-many
# relations for which there is poor support in Django 1.2
docs_dict = dict((d.pk, d) for d in docs)
doc_ids = docs_dict.keys()
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=doc_ids).values_list("document_id", "name"))
# latest event cache
event_types = ("published_rfc",
"changed_ballot_position",
"started_iesg_process",
"new_revision")
for d in docs:
d.latest_event_cache = dict()
for e in event_types:
d.latest_event_cache[e] = None
for e in DocEvent.objects.filter(doc__in=doc_ids, type__in=event_types).order_by('time'):
docs_dict[e.doc_id].latest_event_cache[e.type] = e
# IPR
for d in docs:
d.iprs = []
ipr_docaliases = IprDocAlias.objects.filter(doc_alias__document__in=doc_ids).select_related('doc_alias')
for a in ipr_docaliases:
docs_dict[a.doc_alias.document_id].iprs.append(a)
# telechat date, can't do this with above query as we need to get TelechatDocEvents out
seen = set()
for e in TelechatDocEvent.objects.filter(doc__in=doc_ids, type="scheduled_for_telechat").order_by('-time'):
if e.doc_id not in seen:
d = docs_dict[e.doc_id]
d.telechat_date = wrap_value(d.telechat_date(e))
seen.add(e.doc_id)
# misc
for d in docs:
# emulate canonical name which is used by a lot of the utils
d.canonical_name = wrap_value(rfc_aliases[d.pk] if d.pk in rfc_aliases else d.name)
if d.rfc_number() != None and d.latest_event_cache["published_rfc"]:
d.latest_revision_date = d.latest_event_cache["published_rfc"].time
elif d.latest_event_cache["new_revision"]:
d.latest_revision_date = d.latest_event_cache["new_revision"].time
else:
d.latest_revision_date = d.time
if d.get_state_slug() == "rfc":
d.search_heading = "RFCs"
elif d.get_state_slug() == "active":
d.search_heading = "Active Internet-Drafts"
else:
d.search_heading = "Old Internet-Drafts"
d.expirable = expirable_draft(d)
if d.get_state_slug() != "rfc":
d.milestones = d.groupmilestone_set.filter(state="active").order_by("time").select_related("group")
# RFCs
# errata
erratas = set(Document.objects.filter(tags="errata", name__in=rfc_aliases.keys()).distinct().values_list("name", flat=True))
for d in docs:
d.has_errata = d.name in erratas
# obsoleted/updated by
for a in rfc_aliases:
d = docs_dict[a]
d.obsoleted_by_list = []
d.updated_by_list = []
xed_by = RelatedDocument.objects.filter(target__name__in=rfc_aliases.values(),
relationship__in=("obs", "updates")).select_related('target__document_id')
rel_rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc",
document__in=[rel.source_id for rel in xed_by]).values_list('document_id', 'name'))
for rel in xed_by:
d = docs_dict[rel.target.document_id]
if rel.relationship_id == "obs":
l = d.obsoleted_by_list
elif rel.relationship_id == "updates":
l = d.updated_by_list
l.append(rel_rfc_aliases[rel.source_id].upper())
l.sort()
def retrieve_search_results(form):
"""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
if not (query['activedrafts'] or query['olddrafts'] or query['rfcs']):
return ([], {})
# Non-ASCII strings don't match anything; this check
# is currently needed to avoid complaints from MySQL.
# FIXME: this should be fixed with MySQL if it's still a problem?
for k in ['name','author','group']:
try:
tmp = str(query.get(k, ''))
except:
query[k] = '*NOSUCH*'
# Start by search InternetDrafts
idresults = []
rfcresults = []
MAX = 500
docs = InternetDraft.objects.all()
docs = Document.objects.filter(type="draft")
# name
if query["name"]:
@ -136,9 +219,9 @@ def search_query(query_original, sort_by=None):
allowed_states = []
if query["rfcs"]:
allowed_states.append("rfc")
if query["activeDrafts"]:
if query["activedrafts"]:
allowed_states.append("active")
if query["oldDrafts"]:
if query["olddrafts"]:
allowed_states.extend(['repl', 'expired', 'auth-rm', 'ietf-rm'])
docs = docs.filter(states__type="draft", states__slug__in=allowed_states)
@ -146,7 +229,6 @@ def search_query(query_original, sort_by=None):
# radio choices
by = query["by"]
if by == "author":
# FIXME: this is full name, not last name as hinted in the HTML
docs = docs.filter(authors__person__name__icontains=query["author"])
elif by == "group":
docs = docs.filter(group__acronym=query["group"])
@ -158,66 +240,20 @@ def search_query(query_original, sort_by=None):
elif by == "state":
if query["state"]:
docs = docs.filter(states=query["state"])
if query["subState"]:
docs = docs.filter(tags=query["subState"])
if query["substate"]:
docs = docs.filter(tags=query["substate"])
# evaluate and fill in values with aggregate queries to avoid
# too many individual queries
# evaluate and fill in attribute results immediately to cut down
# the number of queries
results = list(docs.select_related("states", "ad", "ad__person", "std_level", "intended_std_level", "group", "stream")[:MAX])
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[r.pk for r in results]).values_list("document_id", "name"))
# canonical name
for r in results:
if r.pk in rfc_aliases:
# lambda weirdness works around lambda binding in local for loop scope
r.canonical_name = (lambda x: lambda: x)(rfc_aliases[r.pk])
else:
r.canonical_name = (lambda x: lambda: x)(r.name)
result_map = dict((r.pk, r) for r in results)
# events
event_types = ("published_rfc",
"changed_ballot_position",
"started_iesg_process",
"new_revision")
for d in rfc_aliases.keys():
for e in event_types:
setattr(result_map[d], e, None)
for e in DocEvent.objects.filter(doc__in=rfc_aliases.keys(), type__in=event_types).order_by('-time'):
r = result_map[e.doc_id]
if not getattr(r, e.type):
# sets e.g. r.published_date = e for use in proxy wrapper
setattr(r, e.type, e)
# obsoleted/updated by
for d in rfc_aliases:
r = result_map[d]
r.obsoleted_by_list = []
r.updated_by_list = []
xed_by = RelatedDocument.objects.filter(target__name__in=rfc_aliases.values(), relationship__in=("obs", "updates")).select_related('target__document_id')
rel_rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[rel.source_id for rel in xed_by]).values_list('document_id', 'name'))
for rel in xed_by:
r = result_map[rel.target.document_id]
if rel.relationship_id == "obs":
attr = "obsoleted_by_list"
else:
attr = "updated_by_list"
getattr(r, attr).append(int(rel_rfc_aliases[rel.source_id][3:]))
fill_in_search_attributes(results)
# sort
def sort_key(d):
res = []
canonical = d.canonical_name()
if canonical.startswith('rfc'):
rfc_num = int(canonical[3:])
else:
rfc_num = None
rfc_num = d.rfc_number()
if rfc_num != None:
res.append(2)
@ -226,20 +262,20 @@ def search_query(query_original, sort_by=None):
else:
res.append(3)
if sort_by == "title":
if query["sort"] == "title":
res.append(d.title)
elif sort_by == "date":
res.append(str(d.revision_date or datetime.date(1990, 1, 1)))
elif sort_by == "status":
elif query["sort"] == "date":
res.append(str(d.latest_revision_date))
elif query["sort"] == "status":
if rfc_num != None:
res.append(rfc_num)
res.append(int(rfc_num))
else:
res.append(d.get_state().order)
elif sort_by == "ipr":
res.append(d.name)
elif sort_by == "ad":
res.append(d.get_state().order if d.get_state() else None)
elif query["sort"] == "ipr":
res.append(len(d.iprs))
elif query["sort"] == "ad":
if rfc_num != None:
res.append(rfc_num)
res.append(int(rfc_num))
elif d.get_state_slug() == "active":
if d.get_state("draft-iesg"):
res.append(d.get_state("draft-iesg").order)
@ -247,286 +283,177 @@ def search_query(query_original, sort_by=None):
res.append(0)
else:
if rfc_num != None:
res.append(rfc_num)
res.append(int(rfc_num))
else:
res.append(canonical)
res.append(d.canonical_name())
return res
results.sort(key=sort_key)
# fill in a meta dict with some information for rendering the result table
meta = {}
if len(docs) == MAX:
if len(results) == MAX:
meta['max'] = MAX
if query['by']:
meta['advanced'] = True
meta['by'] = query['by']
meta['advanced'] = bool(query['by'])
# finally wrap in old wrappers
meta['headers'] = [{'title': 'Document', 'key':'document'},
{'title': 'Title', 'key':'title'},
{'title': 'Date', 'key':'date'},
{'title': 'Status', 'key':'status', 'colspan':'2'},
{'title': 'IPR', 'key':'ipr'},
{'title': 'Area Director', 'key':'ad'}]
wrapped_results = []
for r in results:
draft = None
rfc = None
if not r.name.startswith('rfc'):
draft = IdWrapper(r)
if r.name.startswith('rfc') or r.pk in rfc_aliases:
rfc = RfcWrapper(r)
wrapped_results.append(IdRfcWrapper(draft, rfc))
if hasattr(form.data, "urlencode"): # form was fed a Django QueryDict, not local plain dict
d = form.data.copy()
for h in meta['headers']:
d["sort"] = h["key"]
h["sort_url"] = "?" + d.urlencode()
if h['key'] == query.get('sort'):
h['sorted'] = True
return (wrapped_results, meta)
return (results, meta)
def generate_query_string(request, ignore_list):
"""Recreates the parameter string from the given request, and
returns it as a string.
Any parameter names present in ignore_list shall not be put
in the result string.
"""
params = []
for i in request.GET:
if not i in ignore_list:
params.append(i + "=" + request.GET[i])
return "?" + "&".join(params)
def search_results(request):
if len(request.REQUEST.items()) == 0:
return search_main(request)
form = SearchForm(dict(request.REQUEST.items()))
if not form.is_valid():
return HttpResponseBadRequest("form not valid?", mimetype="text/plain")
def search(request):
if request.GET:
# backwards compatibility
get_params = request.GET.copy()
if 'activeDrafts' in request.GET:
get_params['activedrafts'] = request.GET['activeDrafts']
if 'oldDrafts' in request.GET:
get_params['olddrafts'] = request.GET['oldDrafts']
if 'subState' in request.GET:
get_params['substate'] = request.GET['subState']
sort_by = None
if "sortBy" in request.GET:
sort_by = request.GET["sortBy"]
form = SearchForm(get_params)
if not form.is_valid():
return HttpResponseBadRequest("form not valid: %s" % form.errors)
(results,meta) = search_query(form.cleaned_data, sort_by)
meta['searching'] = True
meta['by'] = form.cleaned_data['by']
meta['rqps'] = generate_query_string(request, ['sortBy'])
# With a later Django we can do this from the template (incude with tag)
# Pass the headers and their sort key names
meta['hdrs'] = [{'htitle': 'Document', 'htype':'doc'},
{'htitle': 'Title', 'htype':'title'},
{'htitle': 'Date', 'htype':'date'},
{'htitle': 'Status', 'htype':'status', 'colspan':'2'},
{'htitle': 'IPR', 'htype':'ipr'},
{'htitle': 'Ad/Shepherd', 'htype':'ad'}]
# Make sure we know which one is selected (for visibility later)
if sort_by:
for hdr in meta['hdrs']:
if hdr['htype'] == sort_by:
hdr['selected'] = True
if 'ajax' in request.REQUEST and request.REQUEST['ajax']:
return render_to_response('idrfc/search_results.html', {'docs':results, 'meta':meta}, context_instance=RequestContext(request))
elif form.cleaned_data['lucky'] and len(results)==1:
doc = results[0]
if doc.id:
return HttpResponsePermanentRedirect(doc.id.get_absolute_url())
else:
return HttpResponsePermanentRedirect(doc.rfc.get_absolute_url())
results, meta = retrieve_search_results(form)
meta['searching'] = True
else:
return render_to_response('idrfc/search_main.html', {'form':form, 'docs':results,'meta':meta}, context_instance=RequestContext(request))
form = SearchForm()
results = []
meta = { 'by': None, 'advanced': False, 'searching': False }
def search_main(request):
form = SearchForm()
return render_to_response('idrfc/search_main.html', {'form':form}, context_instance=RequestContext(request))
return render_to_response('doc/search.html',
{'form':form, 'docs':results, 'meta':meta, 'show_add_to_list': True },
context_instance=RequestContext(request))
def by_ad(request, name):
ad_id = None
ad_name = None
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
responsible = Document.objects.values_list('ad', flat=True).distinct()
for p in Person.objects.filter(Q(role__name__in=("pre-ad", "ad"),
role__group__type="area",
role__group__state="active")
| Q(pk__in=responsible)).distinct():
if name == p.full_name_as_key():
ad_id = p.id
ad_name = p.plain_name()
break
else:
for i in IESGLogin.objects.filter(user_level__in=[1,2]):
iname = str(i).lower().replace(' ','.')
if name == iname:
ad_id = i.id
ad_name = str(i)
break
if not ad_id:
raise Http404
form = SearchForm({'by':'ad','ad':ad_id,
'rfcs':'on', 'activeDrafts':'on', 'oldDrafts':'on'})
if not form.is_valid():
raise ValueError("form did not validate")
(results,meta) = search_query(form.cleaned_data)
results.sort(key=lambda obj: obj.view_sort_key_byad())
return render_to_response('idrfc/by_ad.html', {'form':form, 'docs':results,'meta':meta, 'ad_name':ad_name}, context_instance=RequestContext(request))
def ad_dashboard_group(doc):
if doc.type.slug=='draft':
if doc.get_state_slug('draft') == 'rfc':
return 'RFC'
elif doc.get_state_slug('draft') == 'active' and doc.get_state_slug('draft-iesg'):
return '%s Internet-Draft' % doc.get_state('draft-iesg').name
else:
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'
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'
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:
return '%s Status Change' % doc.get_state('statchg')
elif doc.type.slug=='charter':
if doc.get_state_slug('charter') == 'approved':
return "Approved Charter"
else:
return '%s Charter' % doc.get_state('charter')
else:
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':
return "22%d" % 0 # TODO - get the date of the transition into this state here
if doc.type.slug=='conflrev' and doc.get_state_slug('conflrev') in ('appr-reqnopub-sent','appr-noprob-sent'):
return "23%d" % 0 # TODO - get the date of the transition into this state here
if doc.type.slug=='charter' and doc.get_state_slug('charter') == 'approved':
return "24%d" % 0 # TODO - get the date of the transition into this state here
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')
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')
return "1%d%s" % (state.order,seed)
elif doc.get_state_slug('charter') == 'extrev':
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')
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')
return "1%d%s" % (state.order,seed)
if seed.startswith('Needs Shepherd'):
return "100%s" % seed
if seed.endswith(' Document'):
seed = seed[:-9]
elif seed.endswith(' Internet-Draft'):
seed = seed[:-15]
elif seed.endswith(' Conflict Review'):
seed = seed[:-16]
elif seed.endswith(' Status Change'):
seed = seed[:-14]
state = State.objects.filter(type__slug='draft-iesg',name=seed)
if state:
ageseconds = 0
changetime= doc.latest_event(type='changed_document')
if changetime:
ad = (datetime.datetime.now()-doc.latest_event(type='changed_document').time)
ageseconds = (ad.microseconds + (ad.seconds + ad.days * 24 * 3600) * 10**6) / 10**6
return "1%d%s%s%010d" % (state[0].order,seed,doc.type.slug,ageseconds)
return "3%s" % seed
def by_ad2(request, name):
def drafts_for_ad(request, name):
ad = None
responsible = Document.objects.values_list('ad', flat=True).distinct()
ad_id = None
for p in Person.objects.filter(Q(role__name__in=("pre-ad", "ad"),
role__group__type="area",
role__group__state="active")
| Q(pk__in=responsible)).distinct():
if name == p.full_name_as_key():
ad_id = p.id
ad_name = p.plain_name()
ad = p
break
if not ad_id:
if not ad:
raise Http404
form = SearchForm({'by':'ad','ad': ad.id,
'rfcs':'on', 'activedrafts':'on', 'olddrafts':'on',
'sort': 'status'})
results, meta = retrieve_search_results(form)
docqueryset = Document.objects.filter(ad__id=ad_id)
docs=[]
for doc in docqueryset:
doc.ad_dashboard_sort_key = ad_dashboard_sort_key(doc)
doc.ad_dashboard_group = ad_dashboard_group(doc)
if doc.get_state_slug() == 'rfc':
doc.display_date = doc.latest_event(type='published_rfc').time
else:
revision = doc.latest_event(type='new_revision')
if revision:
doc.display_date = revision.time
# This might be better handled as something Documents know about themselves
now = datetime.datetime.now()
doc.can_expire = (doc.type.slug=='draft' and doc.get_state_slug('draft')=='active' and ( not doc.get_state('draft-iesg') or doc.get_state('draft-iesg').order >= 42) and doc.expires>now)
if doc.get_state_slug('draft') == 'rfc':
doc.obsoleted_by = ", ".join([ 'RFC %04d' % int(rel.source.rfc_number()) for alias in doc.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='obsoletes') ] )
doc.updated_by = ", ".join([ 'RFC %04d' % int(rel.source.rfc_number()) for alias in doc.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='updates') ] )
doc.has_errata = bool(doc.tags.filter(slug="errata"))
else:
s = doc.get_state("draft-rfceditor")
if s:
# extract possible extra annotations
tags = doc.tags.filter(slug__in=("iana", "ref"))
doc.rfc_editor_state = "*".join([s.name] + [t.slug.upper() for t in tags])
if doc.type.slug == 'draft':
doc.iprCount = IprDraft.objects.filter(document=doc, ipr__status__in=[1,3]).count()
doc.iprUrl = "/ipr/search?option=document_search&id_document_tag=%s" % doc.name
docs.append(doc)
docs.sort(key=ad_dashboard_sort_key)
return render_to_response('idrfc/by_ad2.html',{'docs':docs,'ad_name':ad_name}, context_instance=RequestContext(request))
for d in results:
if d.get_state_slug() == "active":
iesg_state = d.get_state("draft-iesg")
if iesg_state:
if iesg_state.slug == "dead":
d.search_heading = "IESG Dead Internet-Drafts"
else:
d.search_heading = "%s Internet-Drafts" % iesg_state.name
return render_to_response('doc/drafts_for_ad.html',
{ 'form':form, 'docs':results, 'meta':meta, 'ad_name': ad.plain_name() },
context_instance=RequestContext(request))
@cache_page(15*60) # 15 minutes
def all(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
active = (dict(filename=n) for n in InternetDraft.objects.filter(states__type="draft", states__slug="active").order_by("name").values_list('name', flat=True))
rfc1 = (dict(filename=d, rfc_number=int(n[3:])) for d, n in DocAlias.objects.filter(document__states__type="draft", document__states__slug="rfc", name__startswith="rfc").exclude(document__name__startswith="rfc").order_by("document__name").values_list('document__name','name').distinct())
rfc2 = (dict(rfc_number=r, draft=None) for r in sorted(int(n[3:]) for n in Document.objects.filter(type="draft", name__startswith="rfc").values_list('name', flat=True)))
dead = InternetDraft.objects.exclude(states__type="draft", states__slug__in=("active", "rfc")).select_related("states").order_by("name")
def drafts_in_last_call(request):
lc_state = State.objects.get(type="draft-iesg", slug="lc").pk
form = SearchForm({'by':'state','state': lc_state, 'rfcs':'on', 'activedrafts':'on'})
results, meta = retrieve_search_results(form)
return render_to_response('doc/drafts_in_last_call.html',
{ 'form':form, 'docs':results, 'meta':meta },
context_instance=RequestContext(request))
def drafts_in_iesg_process(request, last_call_only=None):
if last_call_only:
states = State.objects.filter(type="draft-iesg", slug__in=("lc", "writeupw", "goaheadw"))
title = "Documents in Last Call"
else:
active = InternetDraft.objects.all().filter(status=1).order_by("filename").values('filename')
rfc1 = InternetDraft.objects.all().filter(status=3).order_by("filename").values('filename','rfc_number')
rfc_numbers1 = InternetDraft.objects.all().filter(status=3).values_list('rfc_number', flat=True)
rfc2 = RfcIndex.objects.all().exclude(rfc_number__in=rfc_numbers1).order_by('rfc_number').values('rfc_number','draft')
dead = InternetDraft.objects.all().exclude(status__in=[1,3]).order_by("filename").select_related('status__status')
return render_to_response('idrfc/all.html', {'active':active, 'rfc1':rfc1, 'rfc2':rfc2, 'dead':dead}, context_instance=RequestContext(request))
states = State.objects.filter(type="draft-iesg").exclude(slug__in=('pub', 'dead', 'watching', 'rfcqueue'))
title = "Documents in IESG process"
@cache_page(15*60) # 15 minutes
def active(request):
groups = IETFWG.objects.exclude(group_acronym=1027)
individual = IETFWG.objects.get(group_acronym=1027)
return render_to_response("idrfc/active.html", {'groups':groups,'individual':individual}, context_instance=RequestContext(request))
grouped_docs = []
def in_last_call(request):
lcdocs = []
for s in states.order_by("order"):
docs = Document.objects.filter(type="draft", states=s).distinct().order_by("time").select_related("ad", "group", "group__parent")
if docs:
if s.slug == "lc":
for d in docs:
e = d.latest_event(LastCallDocEvent, type="sent_last_call")
d.lc_expires = e.expires if e else datetime.datetime.min
docs = list(docs)
docs.sort(key=lambda d: d.lc_expires)
for p in InternetDraft.objects.all().filter(idinternal__primary_flag=1).filter(idinternal__cur_state__state='In Last Call'):
if (p.idinternal.rfc_flag):
lcdocs.append(IdRfcWrapper(None,RfcWrapper(p)))
else:
lcdocs.append(IdRfcWrapper(IdWrapper(p),None))
grouped_docs.append((s, docs))
return render_to_response("idrfc/in_last_call.html", {'lcdocs':lcdocs}, context_instance=RequestContext(request))
return render_to_response('doc/drafts_in_iesg_process.html', {
"grouped_docs": grouped_docs,
"title": title,
"last_call_only": last_call_only,
}, context_instance=RequestContext(request))
def index_all_drafts(request):
# try to be efficient since this view returns a lot of data
categories = []
for s in ("active", "rfc", "expired", "repl", "auth-rm", "ietf-rm"):
state = State.objects.get(type="draft", slug=s)
if state.slug == "rfc":
heading = "RFCs"
elif state.slug in ("ietf-rm", "auth-rm"):
heading = "Internet-Drafts %s" % state.name
else:
heading = "%s Internet-Drafts" % state.name
draft_names = DocAlias.objects.filter(document__states=state).values_list("name", "document")
names = []
names_to_skip = set()
for name, doc in draft_names:
sort_key = name
if name != doc:
if not name.startswith("rfc"):
name, doc = doc, name
names_to_skip.add(doc)
if name.startswith("rfc"):
name = name.upper()
sort_key = -int(name[3:])
names.append((name, sort_key))
names.sort(key=lambda t: t[1])
names = ['<a href="/doc/' + name + '/">' + name +'</a>'
for name, _ in names if name not in names_to_skip]
categories.append((state,
heading,
len(names),
"<br>".join(names)
))
return render_to_response('doc/index_all_drafts.html', { "categories": categories },
context_instance=RequestContext(request))
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))

View file

@ -1,56 +0,0 @@
form_for_model(IDInternal) will autogenerate form elemnts to edit the model
class ImageAddForm(BaseForm):
def __init__(self, *args, name=None, **kwargs):
super(ImageAddForm, self).__init__(*args, **kwargs)
self.fields['category'].choices=(('a','a'),)
# create filter based on name=
ImageForm = form_for_model(Image, form=ImageAddForm)
foo = ImageForm(name='foo')
To get from draft to author list:
>>> d = a[5]
>>> print d
draft-fenner-zinin-rtg-standard-reqts
>>> print d.authors.all()
[<IDAuthors: IDAuthors object>, <IDAuthors: IDAuthors object>]
>>> l=d.authors.all()
>>> print l[0].person
Bill Fenner
>>> print l[0].person.emailaddresses_set.filter(priority=d.id_document_tag)
[<EmailAddresses: EmailAddresses object>]
>>> print l[0].person.emailaddresses_set.filter(priority=d.id_document_tag)[0].ail_address
fenner@research.att.com
IDAuthors should have an auxilliary function for this.
It's the one that has the person linkage and knows the document.
--
we should use a variant of django-registration.
http://www.stonemind.net/blog/2007/04/13/django-registration-for-newbies/
1. verify email address with round trip
2. if there's a row in iesg_login, use that username
(? liaison tool logins ?)
otherwise, force the email address
3. get a password and create the user
(this is almost the same as resetting the password)
4. find the person_or_org_info row, associate that with
the user row
--
<ubernostrum> Both the regular and date-base object_detail can take either an
+object_id, or a slug/slug_field combo.
<ubernostrum> So use the username as the "slug" and specify 'username' as the
+'slug_field' argument.
http://www.b-list.org/weblog/2006/11/16/django-tips-get-most-out-generic-views
newly learned: 'slug_field' just gets passed so can be otherdb__username

View file

@ -1,72 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf import settings
from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
from django.utils.feedgenerator import Atom1Feed
from ietf.idtracker.models import IDInternal
import datetime
import re
class DocumentComments(Feed):
feed_type = Atom1Feed
def get_object(self, bits):
if len(bits) != 1:
raise IDInternal.DoesNotExist
rfc = re.match('rfc(\d+)', bits[0])
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return IDInternal.objects.get(docalias__name=bits[0])
if rfc:
return IDInternal.objects.get(draft=int(rfc.group(1)), rfc_flag=1)
else:
return IDInternal.objects.get(draft__filename=bits[0], rfc_flag=0)
def title(self, obj):
# filename is a function for RFCs and an attribute for I-Ds.
# This works transparently for templates but is not transparent
# for python.
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return "I-D Tracker comments for %s" % obj.filename
if obj.rfc_flag:
filename = obj.document().filename()
else:
filename = obj.document().filename
return "I-D Tracker comments for %s" % filename
def link(self, obj):
if obj is None:
raise FeedDoesNotExist
return obj.get_absolute_url()
def description(self, obj):
return self.title(obj)
def items(self, obj):
return obj.public_comments().order_by("-date","-id")
def item_pubdate(self, item):
return item.datetime()
def item_author_name(self, item):
return item.get_author()
class InLastCall(Feed):
title = "Documents in Last Call"
feed_type = Atom1Feed
author_name = 'IESG Secretary'
link = "/idtracker/status/last-call/"
def items(self):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
ret = list(IDInternal.objects.filter(states__type="draft-iesg", states__slug="lc"))
else:
ret = list(IDInternal.objects.filter(primary_flag=1).filter(cur_state__state='In Last Call'))
ret.sort(key=lambda item: (item.document().lc_expiration_date or datetime.date.today()))
return ret
def item_pubdate(self, item):
# this method needs to return a datetime instance, even
# though the database has only date, not time
return datetime.datetime.combine((item.document().lc_sent_date or datetime.datetime.now().date()), datetime.time(0,0,0))

View file

@ -1 +0,0 @@
/*.pyc

View file

@ -1,151 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0">
<object pk="4" model="idtracker.personororginfo">
<field type="CharField" name="record_type"></field>
<field type="CharField" name="name_prefix"></field>
<field type="CharField" name="first_name">Hermey</field>
<field type="CharField" name="first_name_key">HERMEY</field>
<field type="CharField" name="middle_initial"></field>
<field type="CharField" name="middle_initial_key"></field>
<field type="CharField" name="last_name">Elf</field>
<field type="CharField" name="last_name_key">ELF</field>
<field type="CharField" name="name_suffix"></field>
<field type="DateField" name="date_modified">2007-07-02</field>
<field type="CharField" name="modified_by"></field>
<field type="DateField" name="date_created">2007-07-02</field>
<field type="CharField" name="created_by"></field>
<field type="CharField" name="address_type"></field>
</object>
<object pk="1" model="idtracker.personororginfo">
<field type="CharField" name="record_type"></field>
<field type="CharField" name="name_prefix"></field>
<field type="CharField" name="first_name">Kris</field>
<field type="CharField" name="first_name_key">KRIS</field>
<field type="CharField" name="middle_initial"></field>
<field type="CharField" name="middle_initial_key"></field>
<field type="CharField" name="last_name">Kringle</field>
<field type="CharField" name="last_name_key">KRINGLE</field>
<field type="CharField" name="name_suffix"></field>
<field type="DateField" name="date_modified">2007-07-02</field>
<field type="CharField" name="modified_by"></field>
<field type="DateField" name="date_created">2007-07-02</field>
<field type="CharField" name="created_by"></field>
<field type="CharField" name="address_type"></field>
</object>
<object pk="2" model="idtracker.personororginfo">
<field type="CharField" name="record_type"></field>
<field type="CharField" name="name_prefix"></field>
<field type="CharField" name="first_name">Snow</field>
<field type="CharField" name="first_name_key">SNOW</field>
<field type="CharField" name="middle_initial"></field>
<field type="CharField" name="middle_initial_key"></field>
<field type="CharField" name="last_name">Miser</field>
<field type="CharField" name="last_name_key">MISER</field>
<field type="CharField" name="name_suffix"></field>
<field type="DateField" name="date_modified">2007-07-02</field>
<field type="CharField" name="modified_by"></field>
<field type="DateField" name="date_created">2007-07-02</field>
<field type="CharField" name="created_by"></field>
<field type="CharField" name="address_type"></field>
</object>
<object pk="3" model="idtracker.personororginfo">
<field type="CharField" name="record_type"></field>
<field type="CharField" name="name_prefix"></field>
<field type="CharField" name="first_name">Rudolph</field>
<field type="CharField" name="first_name_key">RUDOLPH</field>
<field type="CharField" name="middle_initial"></field>
<field type="CharField" name="middle_initial_key"></field>
<field type="CharField" name="last_name">Reindeer</field>
<field type="CharField" name="last_name_key">REINDEER</field>
<field type="CharField" name="name_suffix"></field>
<field type="DateField" name="date_modified">2007-07-02</field>
<field type="CharField" name="modified_by"></field>
<field type="DateField" name="date_created">2007-07-02</field>
<field type="CharField" name="created_by"></field>
<field type="CharField" name="address_type"></field>
</object>
<object pk="1" model="idtracker.wgchair">
<field to="idtracker.personororginfo" name="person" rel="ManyToOneRel">1</field>
<field to="idtracker.ietfwg" name="group_acronym" rel="ManyToOneRel">1</field>
</object>
<object pk="2" model="idtracker.wgchair">
<field to="idtracker.personororginfo" name="person" rel="ManyToOneRel">2</field>
<field to="idtracker.ietfwg" name="group_acronym" rel="ManyToOneRel">2</field>
</object>
<object pk="1" model="idtracker.ietfwg">
<field to="idtracker.wgtype" name="group_type" rel="ManyToOneRel">1</field>
<field type="DateField" name="proposed_date"><None></None></field>
<field type="DateField" name="start_date"><None></None></field>
<field type="DateField" name="dormant_date"><None></None></field>
<field type="DateField" name="concluded_date"><None></None></field>
<field to="idtracker.wgstatus" name="status" rel="ManyToOneRel">1</field>
<field to="idtracker.areadirector" name="area_director" rel="ManyToOneRel"><None></None></field>
<field type="CharField" name="meeting_scheduled"></field>
<field type="CharField" name="email_address"></field>
<field type="CharField" name="email_subscribe"></field>
<field type="CharField" name="email_keyword"></field>
<field type="CharField" name="email_archive"></field>
<field type="TextField" name="comments"></field>
<field type="DateField" name="last_modified_date">2007-07-02</field>
<field type="CharField" name="meeting_scheduled_old"></field>
</object>
<object pk="2" model="idtracker.ietfwg">
<field to="idtracker.wgtype" name="group_type" rel="ManyToOneRel">3</field>
<field type="DateField" name="proposed_date"><None></None></field>
<field type="DateField" name="start_date"><None></None></field>
<field type="DateField" name="dormant_date"><None></None></field>
<field type="DateField" name="concluded_date"><None></None></field>
<field to="idtracker.wgstatus" name="status" rel="ManyToOneRel">1</field>
<field to="idtracker.areadirector" name="area_director" rel="ManyToOneRel"><None></None></field>
<field type="CharField" name="meeting_scheduled"></field>
<field type="CharField" name="email_address"></field>
<field type="CharField" name="email_subscribe"></field>
<field type="CharField" name="email_keyword"></field>
<field type="CharField" name="email_archive"></field>
<field type="TextField" name="comments"></field>
<field type="DateField" name="last_modified_date">2007-07-02</field>
<field type="CharField" name="meeting_scheduled_old"></field>
</object>
<object pk="1" model="idtracker.acronym">
<field type="CharField" name="acronym">xmas</field>
<field type="CharField" name="name">Christmas</field>
<field type="CharField" name="name_key">CHRISTMAS</field>
</object>
<object pk="2" model="idtracker.acronym">
<field type="CharField" name="acronym">snow</field>
<field type="CharField" name="name">Silly New Operational Work</field>
<field type="CharField" name="name_key">SILLY NEW OPERATIONAL WORK</field>
</object>
<object pk="1" model="idtracker.wgtype">
<field type="CharField" name="type">WG</field>
</object>
<object pk="2" model="idtracker.wgtype">
<field type="CharField" name="type">PWG</field>
</object>
<object pk="3" model="idtracker.wgtype">
<field type="CharField" name="type">BOF</field>
</object>
<object pk="4" model="idtracker.wgtype">
<field type="CharField" name="type">AG</field>
</object>
<object pk="5" model="idtracker.wgtype">
<field type="CharField" name="type">TEAM</field>
</object>
<object pk="1" model="idtracker.wgstatus">
<field type="CharField" name="status">Active</field>
</object>
<object pk="2" model="idtracker.wgstatus">
<field type="CharField" name="status">Dormant</field>
</object>
<object pk="3" model="idtracker.wgstatus">
<field type="CharField" name="status">Concluded</field>
</object>
<object pk="1" model="idtracker.wgtechadvisor">
<field to="idtracker.ietfwg" name="group_acronym" rel="ManyToOneRel">1</field>
<field to="idtracker.personororginfo" name="person" rel="ManyToOneRel">3</field>
</object>
<object pk="1" model="idtracker.wgsecretary">
<field to="idtracker.ietfwg" name="group_acronym" rel="ManyToOneRel">1</field>
<field to="idtracker.personororginfo" name="person" rel="ManyToOneRel">4</field>
</object>
</django-objects>

View file

@ -1,22 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved
#
from django.contrib.sitemaps import Sitemap
from django.conf import settings
from ietf.idtracker.models import IDInternal, InternetDraft
class IDTrackerMap(Sitemap):
changefreq = "always"
def items(self):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return IDInternal.objects.all()
else:
return IDInternal.objects.exclude(draft=999999)
class DraftMap(Sitemap):
changefreq = "always"
def items(self):
return InternetDraft.objects.all()
def location(self, obj):
return "/drafts/%s/" % obj.filename
def lastmod(self, obj):
return obj.last_modified_date

View file

@ -31,9 +31,18 @@ def expand_comma(value):
def format_charter(value):
return value.replace("\n\n", "</p><p>").replace("\n","<br/>\n")
@register.filter(name='indent')
def indent(value,numspaces=2):
return value.replace("\n", "\n"+" "*int(numspaces));
@register.filter
def indent(value, numspaces=2):
replacement = "\n" + " " * int(numspaces)
res = value.replace("\n", replacement)
if res.endswith(replacement):
res = res[:-int(numspaces)] # fix up superfluous spaces
return res
@register.filter
def unindent(value):
"""Remove indentation from string."""
return re.sub("\n +", "\n", value)
@register.filter(name='parse_email_list')
def parse_email_list(value):
@ -241,6 +250,11 @@ def dashify(string):
"""
return re.sub('.', '-', string)
@register.filter
def underline(string):
"""Return string with an extra line underneath of dashes, for plain text underlining."""
return string + "\n" + ("-" * len(string))
@register.filter(name='lstrip')
def lstripw(string, chars):
"""Strip matching leading characters from words in string"""
@ -320,23 +334,6 @@ def wrap_text(text, width=72):
prev_indent = indent
return "\n".join(filled)
@register.filter(name="id_index_file_types")
def id_index_file_types(text):
r = ".txt"
if text.find("txt") < 0:
return r
if text.find("ps") >= 0:
r = r + ",.ps"
if text.find("pdf") >= 0:
r = r + ",.pdf"
return r
@register.filter(name="id_index_wrap")
def id_index_wrap(text):
x = wordwrap(text, 72)
x = x.replace("\n", "\n ")
return " "+x.strip()
@register.filter(name="compress_empty_lines")
def compress_empty_lines(text):
text = re.sub("( *\n){3,}", "\n\n", text)
@ -408,14 +405,6 @@ def expires_soon(x,request):
days = 14
return x > -days
@register.filter(name='greater_than')
def greater_than(x, y):
return x > int(y)
@register.filter(name='less_than')
def less_than(x, y):
return x < int(y)
@register.filter(name='equal')
def equal(x, y):
return str(x)==str(y)
@ -470,32 +459,22 @@ def format_history_text(text):
full = mark_safe(keep_spacing(linebreaksbr(urlize(sanitize_html(full)))))
snippet = truncate_html_words(full, 25)
if snippet != full:
return mark_safe(u'<div class="snippet">%s<span class="showAll">[show all]</span></div><div style="display:none" class="full">%s</div>' % (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 full
@register.filter
def user_roles_json(user):
roles = {}
if not isinstance(user, basestring) and user.is_authenticated():
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.group.models import Role
for r in Role.objects.filter(person__user=user).select_related(depth=1):
if r.name_id == "secr" and r.group.acronym == "secretariat":
roles["Secretariat"] = True
elif r.name_id == "ad" and r.group.type_id == "area" and r.group.state_id == "active":
roles["Area Director"] = roles["Area_Director"] = True
else:
for g in user.groups.all():
roles[g.name] = True
return mark_safe(simplejson.dumps(roles))
@register.filter
def textify(text):
text = re.sub("</?b>", "*", text)
text = re.sub("</?i>", "/", text)
# There are probably additional conversions we should apply here
return text
@register.filter
def state(doc, slug):
if slug == "stream": # convenient shorthand
slug = "%s-stream-%s" % (doc.type_id, doc.stream_id)
return doc.get_state(slug)
def _test():
import doctest
doctest.testmod()

View file

@ -1,29 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django import template
from ietf import __date__, __rev__, __version__, __id__
register = template.Library()
@register.simple_tag
def revision_time():
return __date__[7:32]
@register.simple_tag
def revision_date():
return __date__[34:-3]
@register.simple_tag
def revision_num():
return __rev__[6:-2]
@register.simple_tag
def revision_id():
return __id__[5:-2]
@register.simple_tag
def version_num():
return __version__

View file

@ -1,42 +1,10 @@
# Copyright The IETF Trust 2007, All Rights Reserved
#
import doctest
import doctest, unittest
from ietf.idtracker.templatetags import ietf_filters
import unittest
from ietf.utils.test_utils import SimpleUrlTestCase, canonicalize_feed, canonicalize_sitemap
import django.test
class TemplateTagTest(unittest.TestCase):
def testTemplateTags(self):
print " Testing ietf_filters"
#doctest.testmod(ietf_filters,verbose=True)
(failures, tests) = doctest.testmod(ietf_filters)
def test_template_tags(self):
failures, tests = doctest.testmod(ietf_filters)
self.assertEqual(failures, 0)
print "OK (ietf_filters)"
class IdTrackerUrlTestCase(SimpleUrlTestCase):
def testUrls(self):
self.doTestUrls(__file__)
def doCanonicalize(self, url, content):
if url.startswith("/feed/"):
return canonicalize_feed(content)
elif url.startswith("/sitemap"):
return canonicalize_sitemap(content)
else:
return content
# class WGRoleTest(django.test.TestCase):
# fixtures = ['wgtest']
#
# def setUp(self):
# from ietf.idtracker.models import IETFWG
# self.xmas = IETFWG.objects.get(group_acronym__acronym='xmas')
# self.snow = IETFWG.objects.get(group_acronym__acronym='snow')
#
# def test_roles(self):
# print " Testing WG roles"
# self.assertEquals(self.xmas.wgchair_set.all()[0].role(), 'xmas WG Chair')
# self.assertEquals(self.snow.wgchair_set.all()[0].role(), 'snow BOF Chair')
# self.assertEquals(self.xmas.wgsecretary_set.all()[0].role(), 'xmas WG Secretary')
# self.assertEquals(self.xmas.wgtechadvisor_set.all()[0].role(), 'xmas Technical Advisor')
# print "OK"

View file

@ -1,25 +0,0 @@
200 /idtracker/help/state/
200 /idtracker/help/state/12/
200 /idtracker/help/substate/1/
301 /idtracker/help/evaluation/
200 /idtracker/status/
200 /idtracker/status/last-call/
301 /idtracker/rfc3847/
301 /idtracker/draft-ietf-isis-link-attr/
301 /idtracker/draft-eronen-tls-psk/ # no IESG information
301 /idtracker/
200 /feed/comments/draft-ietf-isis-link-attr/
200 /feed/comments/rfc3373/
200 /feed/last-call/
# An RFC with no matching value in InternetDrafts. This tests
# subtle cases of using the draft relation when it's not appropriate.
# See ticket #218.
301 /idtracker/rfc2444/
200 /feed/comments/rfc2444/
# Test case for missing comment time (bug fixed in changeset 1733)
200 /feed/comments/draft-ietf-msec-newtype-keyid/
#200,heavy /sitemap-idtracker.xml

View file

@ -1,26 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf.urls.defaults import patterns, url
from ietf.idtracker.models import IDState, IDSubState
from ietf.idtracker import views
from django.views.generic.simple import redirect_to
urlpatterns = patterns('django.views.generic.simple',
url(r'^help/state/$', 'direct_to_template', { 'template': 'idtracker/states.html', 'extra_context': { 'states': IDState.objects.all(), 'substates': IDSubState.objects.all() } }, name="help_states"),
(r'^help/evaluation/$', redirect_to, {'url':'http://www.ietf.org/iesg/voting-procedures.html' }),
)
urlpatterns += patterns('',
(r'^status/$', views.status),
(r'^status/last-call/$', views.last_call),
)
urlpatterns += patterns('',
(r'^rfc0*(?P<rfc_number>\d+)/$', views.redirect_rfc),
(r'^(?P<object_id>\d+)/$', views.redirect_id),
(r'^(?P<filename>[^/]+)/$', views.redirect_filename),
(r'^comment/(?P<object_id>\d+)/$', views.redirect_comment),
(r'^ballot/(?P<object_id>\d+)/$', views.redirect_ballot),
(r'^([^/]+)/comment/(?P<object_id>\d+)/$', views.redirect_comment),
(r'^help/state/(?P<state>\d+)/$', views.state_desc),
(r'^help/substate/(?P<state>\d+)/$', views.state_desc, { 'is_substate': 1 }),
(r'^$', redirect_to, { 'url': '/doc/'}),
)

View file

@ -1,89 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved
# Create your views here.
from django.http import HttpResponsePermanentRedirect, Http404
from django.conf import settings
from django.template import RequestContext
from django.shortcuts import get_object_or_404, render_to_response
from django.views.generic.list_detail import object_detail, object_list
from ietf.idtracker.models import InternetDraft, IDInternal, IDState, IDSubState, BallotInfo, DocumentComment
import re, datetime
def state_desc(request, state, is_substate=0):
if int(state) == 100:
object = {
'state': 'I-D Exists',
'description': """
Initial (default) state for all internet drafts. Such documents are
not being tracked by the IESG as no request has been made of the
IESG to do anything with the document.
"""
}
elif is_substate:
sub = get_object_or_404(IDSubState, pk=state)
object = { 'state': sub.sub_state, 'description': sub.description }
else:
object = get_object_or_404(IDState, pk=state)
return render_to_response('idtracker/state_desc.html', {'state': object},
context_instance=RequestContext(request))
def status(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
drafts = IDInternal.objects.filter(states__type="draft-iesg").exclude(states__type="draft-iesg", states__slug__in=('pub', 'dead', 'watching', 'rfcqueue')).distinct().order_by('states__order')
drafts = [ d for d in drafts if not d.replaced_by ]
drafts.sort(key=lambda d: (d.cur_state_id, d.status_date or datetime.date.min, d.b_sent_date or datetime.date.min))
# sadly we can't use the generic view because it only works with a queryset...
return render_to_response('idtracker/status_of_items.html', dict(object_list=drafts, title="IESG Status of Items"), context_instance=RequestContext(request))
queryset = IDInternal.objects.filter(primary_flag=1).exclude(cur_state__state__in=('RFC Ed Queue', 'RFC Published', 'AD is watching', 'Dead')).order_by('cur_state', 'status_date', 'ballot')
return object_list(request, template_name="idtracker/status_of_items.html", queryset=queryset, extra_context={'title': 'IESG Status of Items'})
def last_call(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
drafts = list(IDInternal.objects.filter(states__type="draft-iesg", states__slug__in=('lc', 'writeupw', 'goaheadw')).distinct().order_by('states__order'))
drafts.sort(key=lambda d: (d.cur_state_id, d.status_date or datetime.date.min, d.b_sent_date or datetime.date.min))
# sadly we can't use the generic view because it only works with a queryset...
return render_to_response('idtracker/status_of_items.html', dict(object_list=drafts, title="Documents in Last Call", lastcall=1), context_instance=RequestContext(request))
queryset = IDInternal.objects.filter(primary_flag=1).filter(cur_state__state__in=('In Last Call', 'Waiting for Writeup', 'Waiting for AD Go-Ahead')).order_by('cur_state', 'status_date', 'ballot')
return object_list(request, template_name="idtracker/status_of_items.html", queryset=queryset, extra_context={'title': 'Documents in Last Call', 'lastcall': 1})
def redirect_id(request, object_id):
'''Redirect from historical document ID to preferred filename url.'''
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
raise Http404() # we don't store the numbers anymore
doc = get_object_or_404(InternetDraft, id_document_tag=object_id)
return HttpResponsePermanentRedirect("/doc/"+doc.filename+"/")
def redirect_rfc(request, rfc_number):
return HttpResponsePermanentRedirect("/doc/rfc"+rfc_number+"/")
def redirect_filename(request, filename):
return HttpResponsePermanentRedirect("/doc/"+filename+"/")
def redirect_ballot(request, object_id):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
raise Http404() # we don't store the numbers anymore
ballot = get_object_or_404(BallotInfo, pk=object_id)
ids = ballot.drafts.filter(primary_flag=1)
if len(ids) == 0:
raise Http404("Ballot does not correspond to any document")
id = ids[0]
if id.rfc_flag:
return HttpResponsePermanentRedirect("/doc/rfc"+str(id.draft_id)+"/#ballot")
else:
return HttpResponsePermanentRedirect("/doc/"+id.draft.filename+"/#ballot")
def redirect_comment(request, object_id):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
raise Http404() # we don't store the numbers anymore
comment = get_object_or_404(DocumentComment, pk=object_id)
id = comment.document
if id.rfc_flag:
return HttpResponsePermanentRedirect("/doc/rfc"+str(id.draft_id)+"/#history-"+str(object_id))
else:
return HttpResponsePermanentRedirect("/doc/"+id.draft.filename+"/#history-"+str(object_id))

View file

@ -505,11 +505,11 @@ class DeferUndeferTestCase(django.test.TestCase):
self.assertEquals(len(q('form.defer')),1)
# defer
self.assertEquals(doc.telechat_date,first_date)
self.assertEquals(doc.telechat_date(), first_date)
r = self.client.post(url,dict())
self.assertEquals(r.status_code, 302)
doc = Document.objects.get(name=name)
self.assertEquals(doc.telechat_date,second_date)
self.assertEquals(doc.telechat_date(), second_date)
self.assertTrue(doc.returning_item())
defer_states = dict(draft=['draft-iesg','defer'],conflrev=['conflrev','defer'])
if doc.type_id in defer_states:
@ -547,11 +547,11 @@ class DeferUndeferTestCase(django.test.TestCase):
self.assertEquals(len(q('form.undefer')),1)
# undefer
self.assertEquals(doc.telechat_date,second_date)
self.assertEquals(doc.telechat_date(), second_date)
r = self.client.post(url,dict())
self.assertEquals(r.status_code, 302)
doc = Document.objects.get(name=name)
self.assertEquals(doc.telechat_date,first_date)
self.assertEquals(doc.telechat_date(), first_date)
self.assertTrue(doc.returning_item())
undefer_states = dict(draft=['draft-iesg','iesg-eva'],conflrev=['conflrev','iesgeval'])
if doc.type_id in undefer_states:

View file

@ -515,8 +515,7 @@ class RescheduleForm(forms.Form):
self.fields['telechat_date'].choices = choices
def handle_reschedule_form(request, doc, dates):
initial = dict(
telechat_date=doc.telechat_date if doc.on_upcoming_agenda() else None)
initial = dict(telechat_date=doc.telechat_date())
formargs = dict(telechat_dates=dates,
prefix="%s" % doc.name,
@ -551,11 +550,11 @@ def agenda_documents(request):
i.reschedule_form = handle_reschedule_form(request, i, dates)
# some may have been taken off the schedule by the reschedule form
docs = filter(lambda x: x.on_upcoming_agenda(), docs)
docs = [d for d in docs if d.telechat_date()]
telechats = []
for date in dates:
matches = filter(lambda x: x.telechat_date == date, docs)
matches = filter(lambda x: x.telechat_date() == date, docs)
res = {}
for i in matches:
section_key = "s" + get_doc_section(i)

View file

@ -2,9 +2,6 @@
from django.db import models
from django.conf import settings
#from django import newforms as forms
from ietf.idtracker.views import InternetDraft
from ietf.idtracker.models import Rfc
from ietf.utils.lazy import reverse_lazy
# ------------------------------------------------------------------------
@ -139,9 +136,6 @@ class IprDetail(models.Model):
return None
except IprContact.MultipleObjectsReturned:
return self.contact.filter(contact_type=3)[0]
class Meta:
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
db_table = 'ipr_detail'
class IprContact(models.Model):
TYPE_CHOICES = (
@ -162,20 +156,8 @@ class IprContact(models.Model):
email = models.EmailField(max_length=255)
def __str__(self):
return self.name or '<no name>'
class Meta:
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
db_table = 'ipr_contacts'
class IprDraft(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='drafts_old' if settings.USE_DB_REDESIGN_PROXY_CLASSES else 'drafts')
document = models.ForeignKey(InternetDraft, db_column='id_document_tag', related_name="ipr_draft_old" if settings.USE_DB_REDESIGN_PROXY_CLASSES else "ipr")
revision = models.CharField(max_length=2)
def __str__(self):
return "%s which applies to %s-%s" % ( self.ipr, self.document, self.revision )
class Meta:
db_table = 'ipr_ids'
class IprNotification(models.Model):
ipr = models.ForeignKey(IprDetail)
notification = models.TextField(blank=True)
@ -183,94 +165,70 @@ class IprNotification(models.Model):
time_sent = models.CharField(blank=True, max_length=25)
def __str__(self):
return "IPR notification for %s sent %s %s" % (self.ipr, self.date_sent, self.time_sent)
class Meta:
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
db_table = 'ipr_notifications'
class IprRfc(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='rfcs_old' if settings.USE_DB_REDESIGN_PROXY_CLASSES else 'rfcs')
document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr_rfc_old" if settings.USE_DB_REDESIGN_PROXY_CLASSES else "ipr")
def __str__(self):
return "%s applies to RFC%04d" % ( self.ipr, self.document_id )
class Meta:
db_table = 'ipr_rfcs'
class IprUpdate(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='updates')
updated = models.ForeignKey(IprDetail, db_column='updated', related_name='updated_by')
status_to_be = models.IntegerField(null=True, blank=True)
processed = models.IntegerField(null=True, blank=True)
from ietf.doc.models import DocAlias
class IprDocAlias(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='documents')
doc_alias = models.ForeignKey(DocAlias)
rev = models.CharField(max_length=2, blank=True)
def __unicode__(self):
if self.rev:
return u"%s which applies to %s-%s" % (self.ipr, self.doc_alias.name, self.rev)
else:
return u"%s which applies to %s" % (self.ipr, self.doc_alias.name)
class Meta:
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
db_table = 'ipr_updates'
verbose_name = "IPR document alias"
verbose_name_plural = "IPR document aliases"
# proxy stuff
if settings.USE_DB_REDESIGN_PROXY_CLASSES or hasattr(settings, "IMPORTING_IPR"):
from ietf.doc.models import DocAlias
class IprDocAlias(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='documents')
doc_alias = models.ForeignKey(DocAlias)
rev = models.CharField(max_length=2, blank=True)
def __unicode__(self):
if self.rev:
return u"%s which applies to %s-%s" % (self.ipr, self.doc_alias.name, self.rev)
else:
return u"%s which applies to %s" % (self.ipr, self.doc_alias.name)
from ietf.utils.proxy import TranslatingManager
class Meta:
verbose_name = "IPR document alias"
verbose_name_plural = "IPR document aliases"
class IprDraftProxy(IprDocAlias):
objects = TranslatingManager(dict(document="doc_alias__name"))
# proxy stuff
IprDraftOld = IprDraft
IprRfcOld = IprRfc
# document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr")
# document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr")
@property
def document(self):
from ietf.doc.proxy import DraftLikeDocAlias
return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id)
from ietf.utils.proxy import TranslatingManager
class IprDraftProxy(IprDocAlias):
objects = TranslatingManager(dict(document="doc_alias__name"))
# document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr")
# document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr")
@property
def document(self):
from ietf.doc.proxy import DraftLikeDocAlias
return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id)
#revision = models.CharField(max_length=2)
@property
def revision(self):
return self.rev
class Meta:
proxy = True
#revision = models.CharField(max_length=2)
@property
def revision(self):
return self.rev
IprDraft = IprDraftProxy
class Meta:
proxy = True
class IprRfcProxy(IprDocAlias):
objects = TranslatingManager(dict(document=lambda v: ("doc_alias__name", "rfc%s" % v)))
# document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr")
# document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr")
@property
def document(self):
from ietf.doc.proxy import DraftLikeDocAlias
return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id)
#revision = models.CharField(max_length=2)
@property
def revision(self):
return self.rev
class Meta:
proxy = True
IprDraft = IprDraftProxy
IprRfc = IprRfcProxy
class IprRfcProxy(IprDocAlias):
objects = TranslatingManager(dict(document=lambda v: ("doc_alias__name", "rfc%s" % v)))
# document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr")
# document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr")
@property
def document(self):
from ietf.doc.proxy import DraftLikeDocAlias
return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id)
# changes done by convert-096.py:changed maxlength to max_length
# removed core
# removed edit_inline
# removed raw_id_admin
#revision = models.CharField(max_length=2)
@property
def revision(self):
return self.rev
class Meta:
proxy = True
IprRfc = IprRfcProxy

View file

@ -15,7 +15,7 @@ urlpatterns = patterns('',
(r'^new-(?P<type>specific)/$', new.new),
(r'^new-(?P<type>generic)/$', new.new),
(r'^new-(?P<type>third-party)/$', new.new),
(r'^search/$', search.search),
url(r'^search/$', search.search, name="ipr_search"),
)

View file

@ -45,10 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% block morecss %}{% endblock %}
</style>
{% block pagehead %}{% endblock %}
<script type="text/javascript">
IETF = {};
IETF.user_groups = {{ user|user_roles_json }};
</script>
{% ifnotequal server_mode "production" %}
<link rel="icon" href="/images/ietf-dev-icon.bmp" />
{% else %}
@ -107,7 +103,6 @@ YAHOO.util.Event.onContentReady("wgs", function () {
{% endblock %}
//]]>
</script>
<script type="text/javascript" src="/js/base.js"></script>
{% block js %}{% endblock %}
{% block content_end %}

View file

@ -38,17 +38,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<ul>
<li class="sect first">Accounts</li>
<li><a href="{% url account_index %}">{% if request.user.is_authenticated %}Manage Account{% else %}New Account{% endif %}</a></li>
{% if user|in_group:"Area_Director" %}
{% if user|has_role:"Area Director" %}
<li class="sect first">AD Dashboard</li>
<li><a href="{% url doc_search_by_ad name=user.get_profile.person.full_name_as_key %}">My Documents (old)</a></li>
<li><a href="{% url doc_search_by_ad2 name=user.get_profile.person.full_name_as_key %}">My Documents (new)</a></li>
<li><a href="{% url drafts_for_ad name=user.get_profile.full_name_as_key %}">My Documents</a></li>
<li><a href="{% url ietf.iesg.views.agenda_documents %}">Next Telechat</a></li>
<li><a href="{% url ietf.iesg.views.discusses %}">Discusses</a></li>
<li><a href="{% url ietf.iesg.views.milestones_needing_review %}">Milestones</a></li>
{# FIXME: this link should be removed when the old WG Actions are completely dead #}
<li><a href="{% url ietf.iesg.views.working_group_actions %}">Working Groups</a></li>
{% endif %}
{% if user|in_group:"Secretariat" %}
{% if user|has_role:"Secretariat" %}
<li class="sect first">Secretariat</li>
<li><a href="/admin/iesg/telechatdate/">Telechat Dates</a></li>
<li><a href="/admin/iesg/telechatagendaitem/">Management Items</a></li>
@ -74,7 +73,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<li class="sect first">RFC Editor</li>
<li><a href="{% url ietf.sync.views.discrepancies %}">Sync discrepancies</a></li>
{% endif %}
<li class="sect{% if not user|in_group:"Area_Director,Secretariat" %} first{% endif %}">Working Groups</li>
<li class="sect{% if not user|has_role:"Area Director,Secretariat" %} first{% endif %}">Working Groups</li>
<li style="padding-bottom:0;"><div id="wgs" class="yuimenu"><div class="bd" style="border:0;">
<ul class="first-of-type" style="padding:0;">
@ -93,7 +92,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<li class="sect">Drafts&nbsp;&amp;&nbsp;RFCs</li>
<li><a href="/doc/">Document search:</a></li>
<li><form action="/doc/search/" method="get" style="padding-bottom:0;margin-bottom:0;"><input type="text" style="margin-left:10px; width:100px; border:1px solid #89d;" name="name" /><input type="hidden" name="activeDrafts" value="on"/><input type="hidden" name="rfcs" value="on"/></form></li>
<li><form action="/doc/search/" method="get" style="padding-bottom:0;margin-bottom:0;"><input type="text" style="margin-left:10px; width:100px; border:1px solid #89d;" name="name" /><input type="hidden" name="activedrafts" value="on"/><input type="hidden" name="rfcs" value="on"/></form></li>
<li><a href="{% url submit_index %}">Submit a draft</a></li>
{% if user|in_group:"WG Chair" %}
<li><a href="{% url submit_approvals %}">Approve a draft</a></li>

View file

@ -0,0 +1,14 @@
{% 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.idrfc.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>

View file

@ -18,7 +18,7 @@ form.start-conflict-review .actions {
{% block content %}
<h1>Begin IETF conflict review for {{doc_to_review.canonical_name}}-{{doc_to_review.rev}}</h1>
<p class="helptext">For help on the initial state choice, see the <a href="{% url help_conflict_review_states %}">state table</a>.</p>
<p class="helptext">For help on the initial state choice, see the <a href="{% url state_help type="conflict-review" %}">state table</a>.</p>
<form class="start-conflict-review" action="" method="post">
<table>

View file

@ -226,10 +226,10 @@
<div class="links">
<a href="mailto:{{ doc.name }}@tools.ietf.org?subject=Mail%20regarding%20{{ doc.name }}" rel="nofollow">Email Authors</a>
| <a href="/ipr/search/?option=document_search&amp;id={{ doc.name }}" rel="nofollow">IPR Disclosures</a>
| <a href="{% url ipr_search %}?option=document_search&amp;id={{ doc.name }}" rel="nofollow">IPR Disclosures</a>
| <a href="http://www.fenron.net/~fenner/ietf/deps/index.cgi?dep={{ name }}" rel="nofollow">Dependencies to this document</a>
| <a href="http://tools.ietf.org/idnits?url=http://tools.ietf.org/id/{{ doc.filename_with_rev }}" rel="nofollow" target="_blank">Check nits</a>
| <a href="/feed/comments/{{ name }}/">History feed</a>
| <a href="/feed/document-changes/{{ name }}/">History feed</a>
| <a href="http://www.google.com/search?as_q={{ doc.name }}&as_sitesearch={{ search_archive }}" rel="nofollow" target="_blank">Search Mailing Lists</a>
{% if user|has_role:"Area Director" %}
| <a href="https://www.iesg.org/bin/c5i?mid=6&rid=77&target={{ doc.name }}" rel="nofollow" target="_blank">Search Mailing Lists (ARO)</a>

View file

@ -6,7 +6,7 @@
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/doc.css"></link>
<link rel="alternate" type="application/atom+xml" href="/feed/comments/{{ doc.canonical_name }}/" />
<link rel="alternate" type="application/atom+xml" href="/feed/document-changes/{{ doc.canonical_name }}/" />
{% endblock %}
{% block content %}

View file

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block title %}Internet-Drafts and RFCs for {{ ad_name }}{% endblock %}
{% block content %}
<h1>Internet-Drafts and RFCs for {{ ad_name }}</h1>
{% include "idrfc/search_results.html" %}
{% endblock %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -0,0 +1,67 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{% block pagehead %}
{% if last_call_only %}<link rel="alternate" type="application/atom+xml" href="/feed/last-call/">{% endif %}
{% endblock %}
{% block morecss %}
th.area, td.area { text-align: left; padding-right: 0.5em; }
th.date, td.date { text-align: left; padding-right: 0.5em; white-space: nowrap; }
{% endblock %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
{% for state, docs in grouped_docs %}
<h2 id="{{ state.slug }}">{{ state.name }}</h2>
<table>
<tr>
<th class="area">Area</th>
<th class="date">{% if state.slug == "lc" %}Expires at{% else %}Date{% endif %}</th>
</tr>
{% for doc in docs %}
<tr class="doc">
<td class="area">{% if doc.area_acronym %}{{ doc.area_acronym.upper }}{% endif %}</td>
<td class="date">
{% if state.slug == "lc" %}
{% if doc.lc_expires %}{{ doc.lc_expires|date:"M j, Y" }}{% endif %}
{% else %}
{{ doc.time|date:"M j, Y" }}
{% endif %}
</td>
<td>{{ doc.title }} ({{ doc.intended_std_level.name }})</td>
</tr>
<tr>
<td></td>
<td></td>
<td><a href="{% url doc_view doc.name %}">{{ doc.name }}</a></td>
</tr>
<tr>
<td></td>
<td>AD:</td>
<td><a href="mailto:{{ doc.ad.email_address|urlencode }}">{{ doc.ad.plain_name }}</a></td>
</tr>
{% if doc.note %}
<tr>
<td></td>
<td>Note:</td>
<td>{{ doc.note|linebreaksbr|urlize }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block title %}Internet-Drafts in IETF Last Call{% endblock %}
{% block content %}
<h1>Internet-Drafts in IETF Last Call</h1>
{% include "idrfc/search_results.html" %}
{% endblock %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block morecss %}
.contents { max-width: 50em; }
.contents a { float: left; width: 7em; margin-right: 2em; }
{% endblock %}
{% block title %}Active Internet-Drafts{% endblock %}
{% block content %}
<h1>Active Internet-Drafts</h1>
<p>This page lists all active Internet-Drafts, grouped by responsible
group. For normal use, it is recommended to use the
<a href="{% url doc_search %}">search page</a>.</p>
<p>There is also an <a href="{% url index_all_drafts %}">index of all
Internet-Drafts</a> (that page also lists some machine-readable files
for download).</p>
<p class="contents">
{% for group in groups %}<a href="#{{ group.acronym }}">{{ group.acronym }}</a> {% endfor %}
</p>
<div style="clear:left"></div>
{% for group in groups %}
<h2 id="{{ group.acronym }}">{{ group.name }} ({{ group.acronym }})</h2>
{% for d in group.active_drafts %}
<p>{{ d.title }}<br>
{% for a in d.authors %}{{ a }}{% if not forloop.last %}, {% endif %}{% endfor %}<br>
<a href="/doc/{{ d.name }}/">{{ d.name }}-{{ d.rev }}</a> ({{ d.rev_time|date:"Y-m-d" }})</p>
{% endfor %}
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,41 @@
{% extends "base.html" %}
{% block title %}Index of all Internet-Drafts and RFCs{% endblock %}
{% block content %}
<h1>Index of all Internet-Drafts and RFCs</h1>
<p>This page lists all Internet-Drafts and RFCs. The main purpose of
this page is to ensure all pages can be found by search engines. For
normal use, it is recommended to use the <a href="{% url doc_search %}">search
page</a>.</p>
<p>There's also an <a href="{% url index_active_drafts %}">index of
active Internet-Drafts</a> with more information.</p>
<p>In addition, the following files are available for download:</p>
<ul>
<li><a href="http://www.ietf.org/id/1id-index.txt">Active Internet-Drafts (text)</a></li>
<li><a href="http://www.ietf.org/id/1id-abstracts.txt">Active Internet-Drafts with abstracts (text)</a></li>
<li><a href="http://www.ietf.org/id/all_id2.txt">All Internet-Drafts (tab-separated)</a>, <a href="http://www.ietf.org/id/all_id.txt">older version (with fewer fields)</a></li>
<li><a href="http://www.rfc-editor.org/rfc/rfc-index.txt">All RFCs (text)</a></li>
<li><a href="http://www.rfc-editor.org/rfc/rfc-index.xml">All RFCs (XML)</a></li>
</ul>
<h2>Contents</h2>
<p>The documents are listed in the following categories:</p>
<ul>
{% for state, heading, count, _ in categories %}
<li><a href="#{{ state.slug }}">{{ heading }} ({{ count }})</a></li>
{% endfor %}
</ul>
{% for state, heading, count, links in categories %}
<h3 id="{{ state.slug }}">{{ heading }} ({{ count }})</h3>
<p class="links">{{ links|safe }}</p>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block title %}Search Internet-Drafts and RFCs{% endblock %}
{% block content %}
<h1>Search Internet-Drafts and RFCs</h1>
<div class="ietf-box search-form-box">
{% include "idrfc/search_form.html" %}
</div>
{% if meta.searching %}{% include "idrfc/search_results.html" %}{% endif %}
{% endblock content %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -0,0 +1,57 @@
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block morecss %}
.ietf-table .name { white-space: nowrap; padding-right: 1em; }
.ietf-table .desc { max-width: 35em; }
{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
{% if state_type.slug == "draft-iesg" %}
<p><a href="/images/iesg_draft_state_diagram.png">View Diagram</a></p>
{% endif %}
<table class="ietf-table">
<tr>
<th>State</th>
<th>Description</th>
{% if has_next_states %}<th>Next State</th>{% endif %}
</tr>
{% for state in states %}
<tr id="{{ state.slug }}" class="{{ forloop.counter|divisibleby:2|yesno:"evenrow,oddrow" }}">
<td class="name">{{ state.name }}</td>
<td class="desc">{{ state.desc|linebreaksbr }}</td>
{% if has_next_states %}
<td class="name">
{% for s in state.next_states.all %}
{{ s.name }}<br/>
{% endfor %}
</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% if tags %}
<h2>Tags</h2>
<table class="ietf-table">
<tr>
<th>Tag</th>
<th>Description</th>
</tr>
{% for tag in tags %}
<tr id="{{ tag.slug }}" class="{{ forloop.counter|divisibleby:2|yesno:"evenrow,oddrow" }}">
<td class="name">{{ tag.name }}</td>
<td class="desc">{{ tag.desc|linebreaksbr }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}

View file

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

View file

@ -1,54 +1,46 @@
{% comment %}
Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the Nokia Corporation and/or its
subsidiary(-ies) nor the names of its contributors may be used
to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}
{% load ietf_filters ietf_streams_redesign %}{% load ballot_icon_redesign %}{% load doc_states %}
{% load ietf_filters %}{% load ballot_icon_redesign %}
<td class="status">
{{ doc.friendly_state|safe }} {% if not doc.rfc %}{{ doc|state_age_colored|safe }}{% endif %}
{% if not hide_telechat_date %}{% if doc.telechat_date %}<br/>IESG Telechat: {{ doc.telechat_date }}{% endif %}{% endif %}
{{ doc.friendly_state|safe }} {% if not doc.get_state_slug == "rfc" %}{{ doc|state_age_colored }}{% endif %}
{% block extra_status %}{% endblock %}
{% if doc.rfc %}
{%comment%}
TODO: Port this block to Document when something that shows RFCs uses this template
{% if doc.rfc.obsoleted_by %}<br />Obsoleted by {{ doc.rfc.obsoleted_by|urlize_ietf_docs }}{%endif %}
{% if doc.rfc.updated_by %}<br />Updated by {{ doc.rfc.updated_by|urlize_ietf_docs }}{%endif %}
{% if doc.rfc.has_errata %}<br /><a href="http://www.rfc-editor.org/errata_search.php?rfc={{doc.rfc.rfc_number}}" rel="nofollow">Errata</a>{% endif %}
{%endcomment%}
{% else %}{# not rfc #}
{% if doc|rfc_editor_state %}<br />RFC Editor State: <a href="http://www.rfc-editor.org/queue2.html#{{doc.name}}">{{ doc|rfc_editor_state|escape }}</a>{% endif %}
{% stream_state doc %}
{% endif %}
{% if not hide_telechat_date and doc.telechat_date %}
<br/>IESG Telechat: {{ doc.telechat_date }}
{% endif %}
{% block extra_status %}{% endblock %}
{% if doc.get_state_slug != "rfc" %}{# I-D #}
{% if doc|state:"draft-rfceditor" %}
<br />RFC Editor State: <a href="http://www.rfc-editor.org/queue2.html#{{ doc.name }}">{{ doc|state:"draft-rfceditor" }}</a>
{% endif %}
{% if doc.stream %}
<br />
{% if doc|state:"stream" %}{{ doc|state:"stream" }}{% else %}{{ doc.stream }}{% endif %}
{% if doc.milestones %}
{% for m in doc.milestones %}<span title="Part of {{ m.group.acronym }} milestone: {{ m.desc }}" class="milestone">{{ m.due|date:"M Y" }}</span>{% endfor %}
{% endif %}
{% endif %}
{% else %}{# RFC #}
{% if doc.obsoleted_by_list %}
<div>Obsoleted by {{ doc.obsoleted_by_list|join:", "|urlize_ietf_docs }}</div>
{% endif %}
{% if doc.updated_by_list %}
<div class="updated-by">Updated by {{ doc.updated_by_list|join:", "|urlize_ietf_docs }}</div>
{% endif %}
{% if doc.has_errata %}
<div><a href="http://www.rfc-editor.org/errata_search.php?rfc={{ doc.rfc_number }}" rel="nofollow">Errata</a></div>
{% endif %}
{% endif %}
</td>
<td class="ballot">
{% ballot_icon doc %}
{% ballot_icon doc %}
</td>

View file

@ -1,3 +0,0 @@
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{{ obj.comment_text|format_textarea|safe|truncatewords_html:"20" }}

View file

@ -1,5 +0,0 @@
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
[{{ obj.get_username }}] {{ obj.comment_text|removetags:"b"|truncatewords:"15"|safe }}
{% if obj.ballot %}[[ IESG {{ obj.get_ballot_display.upper }} ]]{% endif %}
{% if not obj.document.rfc_flag %}[ version {{ obj.version }} ]{% endif %}

View file

@ -1,3 +0,0 @@
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{{ obj.ballot.last_call_text|escape|linebreaks }}

View file

@ -1,2 +0,0 @@
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{{ obj }} ({{ obj.document.lc_sent_date|date:"F j" }}-{{ obj.document.lc_expiration_date|date:"F j, Y" }})

View file

@ -4,8 +4,7 @@
#
# Description of fields:
# 0 draft name and latest revision
# 1 id_document_tag (internal database identifier; avoid using
# unless you really need it)
# 1 always -1 (was internal numeric database id in earlier schema)
# 2 one of "Active", "Expired", "RFC", "Withdrawn by Submitter",
# "Replaced", or "Withdrawn by IETF"
# 3 if #2 is "Active", the IESG state for the document (such as

View file

@ -1,17 +0,0 @@
Internet-Drafts Status Summary
{% for item in in_track_ids %}{{ item.filename }}-{{ item.revision_display }} {{ item.revision_date|default_if_none:"" }} In IESG processing - ID Tracker state <{{ item.idstate }}> {# that last tab is on purpose #}
{% endfor %}{%comment%}
{%endcomment%}{% for item in active %}{{ item.filename }}-{{ item.revision_display }} {{ item.revision_date|default_if_none:"" }} {{ item.status.status }} {# keep that last tab #}
{% endfor %}{%comment%}
{%endcomment%}{% for item in published %}{{ item.filename }}-{{ item.revision_display }} {{ item.revision_date|default_if_none:"" }} {{ item.status.status }} {{ item.rfc_number }}
{% endfor %}{%comment%}
{%endcomment%}{% for item in expired %}{{ item.filename }}-{{ item.revision_display }} {{ item.revision_date|default_if_none:"" }} {{ item.status.status }} {# keep that last tab #}
{% endfor %}{%comment%}
{%endcomment%}{% for item in withdrawn_submitter %}{{ item.filename }}-{{ item.revision_display }} {{ item.revision_date|default_if_none:"" }} {{ item.status.status }} {# keep that last tab #}
{% endfor %}{%comment%}
{%endcomment%}{% for item in withdrawn_ietf %}{{ item.filename }}-{{ item.revision_display }} {{ item.revision_date|default_if_none:"" }} {{ item.status.status }} {# keep that last tab #}
{% endfor %}{%comment%}
{%endcomment%}{% for item in replaced %}{{ item.filename }}-{{ item.revision_display }} {{ item.revision_date|default_if_none:"" }} {{ item.status.status }} replaced by {% if item.replaced_by_id %}{{ item.replaced_by.filename }}{% else %}0{% endif %} {# and this one needs the trailing tab as well #}
{% endfor %}

View file

@ -1,9 +0,0 @@
{% extends "idindex/id_index.txt" %}{% load ietf_filters %} {% block intro %} Current Internet-Drafts
This summary sheet provides a short synopsis of each Internet-Draft
available within the "internet-drafts" directory at the shadow
sites directory. These drafts are listed alphabetically by working
group acronym and start date. Generated {{ time }}
{% endblock %}{% block abstract %}
{{ draft.clean_abstract|indent|indent|safe }}{% endblock %}

View file

@ -1,13 +1,12 @@
{% autoescape off %}{% load ietf_filters %}{% block intro %} Current Internet-Drafts
This summary sheet provides an index of each Internet-Draft
These drafts are listed alphabetically by Working Group acronym and
initial post date.
{% endblock %}
{% for group in groups|dictsort:"group_acronym.acronym" %}{% if group.active_drafts %}
{{ group.group_acronym.name }} ({{ group.group_acronym.acronym}})
{% filter dashify %}{{ group.group_acronym.name }} ({{ group.group_acronym.acronym}}){% endfilter %}
{% for draft in group.active_drafts|stable_dictsort:"filename"|stable_dictsort:"start_date" %}
{% filter id_index_wrap %}
"{{draft.title.strip|clean_whitespace}}", {% for author in draft.authors.all|dictsort:"final_author_order" %}{{author.person}}, {% endfor %}{{draft.revision_date|date:"j-M-y"}}, <{{draft.filename}}-{{draft.revision}}{{draft.file_type|id_index_file_types}}>
{% endfilter %}{% block abstract %}{% endblock %}
{% endfor %}{%endif %}{% endfor %}{% endautoescape %}
{% autoescape off %}{% load ietf_filters %} Current Internet-Drafts
This summary sheet provides an index of each Internet-Draft. These
drafts are listed alphabetically by Working Group acronym and initial
post date. Generated {{ time }}.
{% for group in groups %}
{% filter underline %}{{ group.name }} ({{ group.acronym }}){% endfilter %}
{% for d in group.active_drafts %}
{% filter wordwrap:72|indent:2 %}"{{ d.title|clean_whitespace }}", {% for a in d.authors %}{{ a }}, {% endfor %}{{ d.rev_time|date:"Y-m-d"}}, <{{ d.name }}-{{ d.rev }}{{ d.exts }}>
{% endfilter %}{% if with_abstracts %}
{{ d.abstract.strip|unindent|fill:72|indent:6 }}{% endif %}{% endfor %}{% endfor %}{% endautoescape %}

View file

@ -58,7 +58,7 @@ the <a href="/doc/">search page</a> instead.</p>
<p><a href="/doc/active/">More information about active Internet-Drafts</a></p>
<p>
{% for doc in active %}<a href="/doc/{{ doc.filename }}/">{{ doc.filename }}</a><br/>
{% for doc in active %}<a href="/doc/{{ doc.filename }}/">{{ doc.filename }}</a><br/>
{% endfor %}
</p>

View file

@ -24,7 +24,7 @@ form.change-state .actions {
{% block content %}
<h1>Change state of {{ doc }}</h1>
<p class="helptext">For help on the states, see the <a href="{% url help_states %}">state table</a>.</p>
<p class="helptext">For help on the states, see the <a href="{% url state_help type="draft-iesg" %}">state table</a>.</p>
<form class="change-state" action="" method="post">
<table>

View file

@ -61,7 +61,7 @@ td.ietf-main-intro { width:200px; background:#fff5df; padding:8px; border:1px so
The IETF Datatracker is the IETF's web system for managing information
about:
<ul>
<li><a href="/doc/">Internet-Drafts and RFCs</a></li>
<li><a href="{% url doc_search %}">Internet-Drafts and RFCs</a></li>
<li><a href="/ipr/">IPR disclosures</a></li>
<li><a href="/liaison/">Liaison statements</a></li>
<li><a href="/meeting/agenda/">Meeting agenda</a> and <a href="/meeting/">session agendas/slides/minutes</a></li>
@ -70,14 +70,9 @@ about:
</td>
</tr>
</table>
<div id="search_results" style="margin-top:8px;">
{% if docs %}
{% include "idrfc/search_results.html" %}
{% endif %}
</div>
{% endblock content %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -32,7 +32,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}
<form id="search_form" class="search_form" action="/doc/search/" method="get">
<form id="search_form" class="search_form" action="{% url doc_search %}" method="get">
<div class="search_field">
<label>Name/number/title:</label> {{ form.name }}
@ -42,18 +42,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<label>Types:</label>
<table id="search_types">
<tr><td><label>{{ form.rfcs }} RFCs</label></td></tr>
<tr><td><label>{{ form.activeDrafts }} Internet-Drafts (active)</label></td></tr>
<tr><td><label>{{ form.oldDrafts }} Internet-Drafts (expired/replaced/withdrawn)</label></td></tr>
<tr><td><label>{{ form.activedrafts }} Internet-Drafts (active)</label></td></tr>
<tr><td><label>{{ form.olddrafts }} Internet-Drafts (expired/replaced/withdrawn)</label></td></tr>
</table>
</div>
{{ form.sort }} {# hidden field #}
<b class="toggle_advanced"><img src="/images/{% if meta.advanced %}minus{% else %}plus{% endif %}.png" alt="" /> Advanced</b>
<div id="search_advanced" style="{% if not meta.advanced %}display:none;{%endif%}">
Additional search criteria:
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="author" {% if meta.by == "author" %}checked="checked"{% endif %}/> Author (last name):</label> {{ form.author }}
<label><input type="radio" class="radio" name="by" value="author" {% if meta.by == "author" %}checked="checked"{% endif %}/> Author:</label> {{ form.author }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="group" {% if meta.by == "group" %}checked="checked"{% endif %}/> WG:</label> {{ form.group }}
@ -65,7 +67,7 @@ Additional search criteria:
<label><input type="radio" class="radio" name="by" value="ad" {% if meta.by == "ad" %}checked="checked"{% endif %}/> Responsible AD:</label> {{ form.ad }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="state" {% if meta.by == "state" %}checked="checked"{% endif %}/> IESG State:</label> {{ form.state }} :: {{ form.subState }}
<label><input type="radio" class="radio" name="by" value="state" {% if meta.by == "state" %}checked="checked"{% endif %}/> IESG State:</label> {{ form.state }} :: {{ form.substate }}
</div>
</div><!-- end of advanced -->

View file

@ -42,13 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% include "idrfc/search_form.html" %}
</div>
<div id="search_results">
<div id="search-results">
{% if meta.searching %}
{% include "idrfc/search_results.html" %}
{% include "idrfc/search-results.html" %}
{% endif %}
</div>
{% endblock content %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -37,30 +37,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<tr class="{{ forloop.counter|divisibleby:2|yesno:"oddrow,evenrow" }}">
{% if show_add_to_list and user.is_authenticated %}
<td class="addtolist">
{% if doc.id %}
<a href="{% url community_add_document doc.id.draft_name %}" title="Add to your personal ID list"><img src="/images/add_to_list.png" alt="Add to your personal ID list" /></a>
{% else %}
<a href="{% url community_add_document doc.rfc.filename %}" title="Add to your personal ID list"><img src="/images/add_to_list.png" alt="Add to your personal ID list" /></a>
{% endif %}
<a href="{% url community_add_document doc.name %}" title="Add to your personal ID list"><img src="/images/add_to_list.png" alt="Add to your personal ID list" /></a>
</td>
{% endif %}
<td class="doc">
{% if doc.rfc %}{{ doc.rfc.displayname_with_link|safe }}
{% if doc.id %}<br />(<a href="{{ doc.id.get_absolute_url }}">{{doc.id.draft_name}}</a>){%endif%}
{% else %}{{ doc.id.displayname_with_link|safe }}
{% endif %}
<a href="{{ doc.get_absolute_url }}">{% if doc.get_state_slug == "rfc" %}RFC {{ doc.rfc_number }}{% else %}{{ doc.name }}-{{ doc.rev }}{% endif %}</a>
{% if doc.get_state_slug == "rfc" and "draft" in doc.name %}
<br/><i>(was {{ doc.name }})</i>
{% endif %}
</td>
<td class="title">{{ doc.title }}</td>
<td class="date">{% if not doc.rfc %}{{ doc.publication_date }}{% else %}{{ doc.publication_date|date:"Y-m" }}{% endif %}
{% if doc.publication_date|timesince_days|new_enough:request %}<br/><span class="ietf-small ietf-highlight-y">{% if not doc.rfc%}<a href="http:{{rfcdiff_prefix}}?url2={{doc.id.draft_name_and_revision}}">new</a>{%else%}new{%endif%}</span>{%endif%}
{% if doc.id and doc.id.expected_expiration_date and doc.id.expected_expiration_date|timesince_days|expires_soon:request %}<br/><span class="ietf-small ietf-highlight-y">expires soon</span>{%endif%}
<td class="date">
{% if doc.get_state_slug == "rfc" %}{{ doc.latest_revision_date|date:"Y-m" }}{% else %}{{ doc.latest_revision_date|date:"Y-m-d" }}{% endif %}
{% if doc.latest_revision_date|timesince_days|new_enough:request %}
<div class="ietf-small ietf-highlight-y"><a{% if doc.get_state_slug != "rfc" %} href="http:{{ rfcdiff_prefix }}?url2={{ doc.name }}-{{ doc.rev }}"{% endif %}>new</a></div>
{% endif %}
{% if doc.get_state_slug == "active" and doc.expirable and doc.expires|timesince_days|expires_soon:request %}
<div class="ietf-small ietf-highlight-y">expires soon</div>
{% endif %}
</td>
{% include "idrfc/status_columns.html" %}
{% include "idrfc/ipr_column.html" %}
{# <td class="ad">{% if doc.ad_name %}{{ doc.ad_name }}{% else %}&nbsp;{% endif %}</td> #}
<td class="ad">{{ doc.ad_name|default:"" }}
{% if doc.id.underlying_document.shepherd %}<br/>{{ doc.id.underlying_document.shepherd.plain_name|default:""}}{% endif %}
{% include "doc/status_columns.html" %}
<td class="ipr">
{% if doc.iprs %}
<a href="{% url ipr_search %}?option=document_search&amp;id={{ doc.name }}">{{ doc.iprs|length }}</a>
{% endif %}
</td>
<td class="ad">{{ doc.ad|default:"" }}</td>
</tr>

View file

@ -32,36 +32,42 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}
{% if meta.max %}
<p><b>Too many documents match the query! Returning partial result only.</b></p>
{% endif %}
<div class="search-results">
{% if not docs %}
<p><b>No documents match your query.</b></p>
<h2>No documents match your query.</h2>
{% else %}
{% if meta.max %}
<h2>Too many documents match the query! Returning partial result only.</h2>
{% endif %}
<table class="ietf-table ietf-doctable">
<tr>
{% if user.is_authenticated %}<th></th>{% endif %}
{% if show_add_to_list and user.is_authenticated %}<th></th>{% endif %}
{% for hdr in meta.hdrs %}
<th class="{{ hdr.htype }}" {% if hdr.colspan %}colspan="{{ hdr.colspan }}"{% endif %}>
{{ hdr.htitle }}
<a href="{{ meta.rqps }}&sortBy={{hdr.htype}}"><img src="/images/sort-header-{% if hdr.selected %}filled{% else %}clear{% endif %}.png"/></a>
{% for h in meta.headers %}
<th class="{{ h.key }}" {% if h.colspan %}colspan="{{ h.colspan }}"{% endif %}>
{% if "sort_url" in h %}
<a href="{{ h.sort_url }}">{{ h.title }}
<img src="/images/sort-header-{% if h.sorted %}filled{% else %}clear{% endif %}.png"/></a>
{% else %}
{{ h.title }}
{% endif %}
</th>
{% endfor %}
</tr>
<!-- <tr><th></th><th class="doc">Document</th><th class="title">Title</th><th class="date">Date</th><th class="status" colspan="2">Status</th><th class="ad">Area Director</th></tr> -->
{% regroup docs by view_sort_group as grouped_docs %}
{% regroup docs by search_heading as grouped_docs %}
{% for doc_group in grouped_docs %}
<tr class="header"><td colspan="7">{{ doc_group.grouper }}s</td></tr>
{% with 1 as show_add_to_list %}
{% for doc in doc_group.list %}
{% include "idrfc/search_result_row.html" %}
{% endfor %}
{% endwith %}
<tr class="header"><td colspan="10">{{ doc_group.grouper }}</td></tr>
{% for doc in doc_group.list %}
{% include "idrfc/search_result_row.html" %}
{% endfor %}
{% endfor %}
</table>
{% endif %}
</div>

View file

@ -31,7 +31,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}
{% load ietf_filters ietf_streams %}{% load ballot_icon %}
{% load ietf_filters ietf_streams %}{% load ballot_icon_redesign %}
<td class="status">
{{ doc.friendly_state|safe }} {% if not doc.rfc %}{{ doc.id|state_age_colored|safe }}{% endif %}
{% if not hide_telechat_date %}{% if doc.telechat_date %}<br/>IESG Telechat: {{ doc.telechat_date }}{% endif %}{% endif %}

View file

@ -1 +0,0 @@
/*.pyc

View file

@ -1,25 +0,0 @@
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
<tr>{% if doc.primary_flag %}
<td>{{ doc.area_acronym.area_acronym.acronym.upper }}</td>
<td nowrap>
{% ifequal doc.cur_state.state "In Last Call" %}
{% if doc.document.lc_expiration_date %}
{{ doc.document.lc_expiration_date|date:"M j" }}, {{ doc.document.lc_expiration_date|date:"Y" }}
{% endif %}
{% else %}
{% if doc.status_date %}
{{ doc.status_date|date:"M j" }}, {{ doc.status_date|date:"Y" }}
{% endif %}
{% endifequal %}</td>
{% else %}
<td></td><td></td>
{% endif %}
<td>{{ doc.document.title|escape }} ({{ doc.document.intended_status|escape }})</td></tr>
<tr><td></td><td></td><td><a href="/doc/{{ doc.document.filename }}/">{{ doc.document.displayname|safe }}</a></td></tr>
{% if doc.primary_flag %}
<tr><td></td><td>Token:</td><td><a href="mailto:{{ doc.token_email|urlencode }}">{{ doc.token_name }}</a></td></tr>
{% if doc.note %}
<tr><td></td><td>Note:</td><td>{{ doc.note|linebreaksbr|urlize }}</td></tr>
{% endif %}
{% endif %}

View file

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

View file

@ -1,61 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% block title %}Internet Draft States{% endblock %}
{% block content %}
<h1>Main I-D States</h1>
<p><a href="/images/state_diagram.png">View Diagram</a></p>
<table class="ietf-table">
<tr>
<th>State</th>
<th>Description</th>
<th>Next State(s)</th>
</tr>
{# XXX I-D Exists should be added to the database #}
<tr class="{% cycle oddrow,evenrow as cycle1 %}">
<tr>
<td>I-D Exists</td>
<td >Initial (default) state for all internet drafts. Such documents are
not being tracked by the IESG as no request has been made of the
IESG to do anything with the document.</td>
<td>
<ul>
<li> AD is watching
<li> Publication Requested</ul>
</td></tr>
{% for state in states %}
<tr class="{% cycle cycle1 %}">
<td>{{ state.state|escape }}</td>
<td>{{ state.description|escape }}</td>
<td>
<ul>
{% for next in state.nextstate.all %}
<li>{{ next.next_state.state }}
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</table>
<h2>Sub States</h2>
<table class="ietf-table">
<tr><th>Sub State Name</th><th>Description</th></tr>
{% for substate in substates %}
<tr class="{% cycle oddrow,evenrow %}">
<td>{{ substate.sub_state|escape }}</td>
<td>{{ substate.description|escape }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View file

@ -1,28 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% block pagehead %}{% if lastcall %}
<link rel="alternate" type="application/atom+xml" href="/feed/last-call/">
{% endif %}{% endblock %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
{% regroup object_list by cur_state_id as grouped %}
{% for state in grouped %}
<a name="grp{{ state.list.0.cur_state }}"><h2>{{ state.list.0.cur_state }}</h2></a>
<table>
<tr><th>Area</th><th>{% ifequal state.list.0.cur_state.state "In Last Call" %}Expires at{% else %}Date{% endifequal %}</th></tr>
{% for doc in state.list %}
{% include "idtracker/document_entry.html" %}
{% if doc.ballot_others %}
{% for doc in doc.ballot_others %}
{% include "idtracker/document_entry.html" %}
{% endfor %}
{% endif %}
{% endfor %}
</table>
{% endfor %}
{% endblock %}

View file

@ -110,3 +110,8 @@ div.agenda-wg { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width
</div>
{% endblock content %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -170,5 +170,7 @@ font-size:80%; font-style:italic;
{% endblock content %}
{% block content_end %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
<script type="text/javascript" src="/js/agenda-documents.js"></script>
{% endblock %}

View file

@ -112,3 +112,8 @@ if (url[1] == 'byme') {
}
{% endif %}{# user in_group #}
{% endblock scripts %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -21,7 +21,7 @@ form.change-state .actions {
<h1>{{ title }}</h1>
{% if not option %}
<p class="helptext">For help on the states, see the <a href="{% url help_charter_states %}">state table</a>.</p>
<p class="helptext">For help on the states, see the <a href="{% url state_help type="charter" %}">state table</a>.</p>
{% endif %}
<form class="change-state" action="" method="post">

View file

@ -4,7 +4,6 @@
{% block content %}
{% load ietf_filters %}
{% load ballot_icon %}
<h1>Bofs</h1>

View file

@ -4,7 +4,7 @@
{% block content %}
{% load ietf_filters %}
{% load ballot_icon %}
{% load ballot_icon_redesign %}
<h1>Chartering or Re-Chartering Working Groups</h1>
@ -51,3 +51,8 @@
{% endif %}
{% endblock %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -35,72 +35,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% block wg_titledetail %}Documents{% endblock %}
{% block wg_content %}
<div class="group-documents">
{% regroup docs by view_sort_group as grouped_docs %}
{% include "idrfc/search_results.html" %}
<table class="ietf-table ietf-doctable" style="margin-top:16px;">
<tr>
{% if user.is_authenticated %}<th></th>{% endif %}
<th class="doc">Document</th><th class="title">Title</th><th class="date">Date</th><th class="status" colspan="2">Status</th><th class="ipr">ipr</th><th class="ad">AD / Shepherd</th>
</tr>
{% for doc_group in grouped_docs %}
<tr class="header"><td colspan="7">{{doc_group.grouper}}s</td></tr>
{% with 1 as show_add_to_list %}
{% for doc in doc_group.list %}
{% include "idrfc/search_result_row.html" %}
{% endfor %}
{% endwith %}
{% endfor %}
</table>
{% regroup docs_related by view_sort_group as grouped_docs_related %}
<table class="ietf-table ietf-doctable" style="margin-top:16px;">
<tr>
{% if user.is_authenticated %}<th></th>{% endif %}
<th class="doc">Related Documents</th><th class="title">Title</th><th class="date">Date</th><th class="status" colspan="2">Status</th><th class="ipr">ipr</th><th class="ad">AD / Shepherd</th>
</tr>
{% for doc_group in grouped_docs_related %}
<tr class="header"><td colspan="7">{{doc_group.grouper}}s</td></tr>
{% with 1 as show_add_to_list %}
{% for doc in doc_group.list %}
{% include "idrfc/search_result_row.html" %}
{% endfor %}
{% endwith %}
{% endfor %}
</table>
{% with docs_related as docs %}{% include "idrfc/search_results.html" %}{% endwith %}
</div>
{% endblock wg_content %}
{% block scripts %}
YAHOO.util.Event.onContentReady("search_submit_button", function () {
var oButton = new YAHOO.widget.Button("search_submit_button", {});
});
(function ($) {
$(document).ready(function () {
$('.addtolist a').click(function() {
var trigger = $(this);
$.ajax({
url: trigger.attr('href'),
type: 'GET',
cache: false,
dataType: 'json',
success: function(response){
if (response.success) {
trigger.replaceWith('added');
}
}
});
return false;
});
});
})(jQuery);
{% endblock scripts %}
{% block js %}
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/doc-search.js"></script>
{% endblock %}

View file

@ -5,13 +5,12 @@ from django.conf.urls.defaults import patterns, include, handler404, handler500
from django.contrib import admin
from ietf.iesg.feeds import IESGAgenda
from ietf.idtracker.feeds import DocumentComments, InLastCall
from ietf.doc.feeds import DocumentChanges, InLastCall
from ietf.ipr.feeds import LatestIprDisclosures
from ietf.proceedings.feeds import LatestWgProceedingsActivity
from ietf.liaisons.feeds import Liaisons
from ietf.wgcharter.feeds import GroupChanges
from ietf.idtracker.sitemaps import IDTrackerMap, DraftMap
from ietf.liaisons.sitemaps import LiaisonMap
from ietf.ipr.sitemaps import IPRMap
from ietf.announcements.sitemaps import NOMCOMAnnouncementsMap
@ -25,7 +24,7 @@ admin.site.disable_action('delete_selected')
feeds = {
'iesg-agenda': IESGAgenda,
'last-call': InLastCall,
'comments': DocumentComments,
'document-changes': DocumentChanges,
'group-changes': GroupChanges,
'ipr': LatestIprDisclosures,
'liaison': Liaisons,
@ -33,16 +32,11 @@ feeds = {
}
sitemaps = {
'idtracker': IDTrackerMap,
'drafts': DraftMap,
'liaison': LiaisonMap,
'ipr': IPRMap,
'nomcom-announcements': NOMCOMAnnouncementsMap,
}
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
del sitemaps['drafts'] # not needed, overlaps sitemaps['idtracker']
urlpatterns = patterns('',
(r'^$', 'ietf.idrfc.views.main'),
(r'^accounts/', include('ietf.ietfauth.urls')),
@ -52,10 +46,11 @@ urlpatterns = patterns('',
(r'^community/', include('ietf.community.urls')),
(r'^cookies/', include('ietf.cookies.urls')),
(r'^doc/', include('ietf.idrfc.urls')),
(r'^drafts/', include('ietf.idindex.urls')),
(r'^drafts/', include('ietf.doc.redirect_drafts_urls')),
(r'^feed/comments/(?P<remainder>.*)/$', 'django.views.generic.simple.redirect_to', { 'url': '/feed/document-changes/%(remainder)s/'}),
(r'^feed/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', { 'feed_dict': feeds}),
(r'^help/', include('ietf.help.urls')),
(r'^idtracker/', include('ietf.idtracker.urls')),
(r'^idtracker/', include('ietf.doc.redirect_idtracker_urls')),
(r'^iesg/', include('ietf.iesg.urls')),
(r'^ipr/', include('ietf.ipr.urls')),
(r'^liaison/', include('ietf.liaisons.urls')),

View file

@ -418,6 +418,7 @@ def make_test_data():
docalias = DocAlias.objects.create(name=doc.name, document=doc)
doc.stream = StreamName.objects.get(slug='irtf')
doc.save()
doc.set_state(State.objects.get(type="draft", slug="active"))
crdoc = Document.objects.create(name='conflict-review-imaginary-irtf-submission', type_id='conflrev', rev='00', notify="fsm@ietf.org")
DocAlias.objects.create(name=crdoc.name, document=crdoc)
crdoc.set_state(State.objects.get(name='Needs Shepherd', type__slug='conflrev'))

View file

@ -38,7 +38,7 @@ from django.http import HttpResponse
from django.conf import settings
from django.core.urlresolvers import reverse as urlreverse
from ietf.idtracker.models import Area, IETFWG
from ietf.idrfc.views_search import SearchForm, search_query
from ietf.idrfc.views_search import SearchForm, retrieve_search_results
from ietf.idrfc.idrfc_wrapper import IdRfcWrapper
from ietf.ipr.models import IprDetail
from ietf.group.models import Group
@ -146,39 +146,34 @@ def wg_documents(request, acronym):
concluded = wg.status_id in [ 2, 3, ]
proposed = (wg.status_id == 4)
form = SearchForm({'by':'group', 'group':str(wg.group_acronym.acronym),
'rfcs':'on', 'activeDrafts':'on'})
if not form.is_valid():
raise ValueError("form did not validate")
(docs,meta) = search_query(form.cleaned_data)
'rfcs':'on', 'activedrafts':'on'})
docs, meta = retrieve_search_results(form)
# get the related docs
form_related = SearchForm({'by':'group', 'name':'-'+str(wg.group_acronym.acronym)+'-', 'activeDrafts':'on'})
if not form_related.is_valid():
raise ValueError("form_related did not validate")
(docs_related,meta_related) = search_query(form_related.cleaned_data)
form_related = SearchForm({'by':'group', 'name':'-'+str(wg.group_acronym.acronym)+'-', 'activedrafts':'on'})
docs_related, meta_related = retrieve_search_results(form_related)
docs_related_pruned = []
for d in docs_related:
parts = d.id.draft_name.split("-", 2);
parts = d.name.split("-", 2);
# canonical form draft-<name|ietf>-wg-etc
if ( len(parts) >= 3):
if parts[1] != "ietf" and parts[2].startswith(wg.group_acronym.acronym+"-"):
if len(parts) >= 3 and parts[1] != "ietf" and parts[2].startswith(wg.group_acronym.acronym + "-"):
docs_related_pruned.append(d)
docs_related = docs_related_pruned
# move call for WG adoption to related
cleaned_docs = []
related_doc_names = set(d.id.draft_name for d in docs_related)
docs_related_names = set(d.name for d in docs_related)
for d in docs:
if d.id and d.id._draft and d.id._draft.stream_id == "ietf" and d.id._draft.get_state_slug("draft-stream-ietf") == "c-adopt":
if d.id.draft_name not in related_doc_names:
if d.stream_id == "ietf" and d.get_state_slug("draft-stream-ietf") == "c-adopt":
if d.name not in docs_related_names:
docs_related.append(d)
else:
cleaned_docs.append(d)
docs = cleaned_docs
docs_related.sort(key=lambda d: d.id.draft_name)
docs_related.sort(key=lambda d: d.name)
return wg, concluded, proposed, docs, meta, docs_related, meta_related

View file

@ -33,6 +33,9 @@
*/
body { margin: 0; }
a img { border: 0; }
.yui-skin-sam h1 {margin: 0.5em 0; font-size: 167%;}
.yui-skin-sam .yui-navset .yui-content {
background: white;
@ -114,7 +117,8 @@ table.ietf-table { border-collapse:collapse; border:1px solid #7f7f7f; }
.ietf-doctable tr.header { border-top: 1px solid #7f7f7f; border-bottom: 1px solid #7f7f7f; border-left: 1px solid white; border-right:2px solid white;}
.ietf-doctable tr.header td {padding: 6px 6px; font-weight: bold; }
.ietf-doctable table { max-width: 1200px; }
.ietf-doctable th { cursor: pointer; white-space: nowrap; }
.ietf-doctable th { white-space: nowrap; }
.ietf-doctable th a { text-decoration: none; color: #fff; }
.ietf-doctable th img { border-style: none; vertical-align: top; }
.ietf-doctable th.doc, .ietf-doctable td.doc { min-width:20em; max-width: 35em; }
.ietf-doctable th.title, .ietf-doctable td.title { min-width: 20em; max-width: 35em; }
@ -123,10 +127,11 @@ table.ietf-table { border-collapse:collapse; border:1px solid #7f7f7f; }
.ietf-doctable th.ipr { font-variant: small-caps; }
.ietf-doctable th.ad, .ietf-doctable td.ad { white-space:nowrap; min-width: 6em; }
.ietf-doctable td.ballot { border-left: hidden; min-width: 37px; }
.ietf-doctable td .updated-by { max-width: 20em; } /* some RFCs have really long lists */
table.ballot_icon { empty-cells: show; padding: 0; border-spacing: 0; border: 1px solid black; border-collapse: collapse; table-layout:fixed; min-width:35px; background:white; }
table.ballot_icon td { border: 1px solid black; height: 7px; width: 6px; padding: 0;}
table.ballot_icon td.my { border: 3px outset black;}
.ietf-doctable .status .milestone,
.ietf-box .stream-state .milestone
{ display: inline-block; font-size: smaller; background-color: #d5dde6; padding: 0 0.2em; margin-left: 0.3em; }
.ietf-small { font-size:85%; }
.ietf-tiny { font-size:70%; }
@ -134,7 +139,8 @@ table.ballot_icon td.my { border: 3px outset black;}
.ietf-highlight-r { padding:0 2px;background:#ffa0a0;}
.ietf-divider { background: #2647a0; color: white; font-size:116%; padding:0.5em 1em; }
table.history .snippet .showAll { color: blue; cursor: pointer; }
table.history .snippet .show-all { color: blue; cursor: pointer; }
.error-text {
font-size: 1.095em;
@ -175,14 +181,18 @@ form table .help {
.warning { color: #a00; }
.position-discuss,
.position-block { background-color: #c00000;}
.position-yes { background-color: #80ff80;}
.position-noobj { background-color: #80ff80;}
.position-abstain { background-color: #ffff00;}
.position-recuse { background-color: #c0c0c0;}
.position-norecord { background-color: #ffffff;}
.position-block { background-color: #c00;}
.position-yes { background-color: #0d0;}
.position-noobj { background-color: #0d0;}
.position-abstain { background-color: #ff0;}
.position-recuse { background-color: #bbb;}
.position-norecord { background-color: #fff;}
.ballot-sidebar { width: 160px; float: left; margin-top: 0.4em; margin-right: 1em; padding: 0.5em; background: #edf5ff; }
.ballot-icon table { empty-cells: show; padding: 0; border-spacing: 0; border: 1px solid #666; border-collapse: collapse; table-layout: fixed; min-width: 35px; background: #fff; }
.ballot-icon table td { border: 1px solid #666; height: 7px; width: 6px; padding: 0;}
.ballot-icon table td.my { border: 3px solid #000;}
.ballot-sidebar { width: 160px; float: left; margin-top: 0.4em; padding: 5px; background: #edf5ff; }
.ballot-sidebar .action { margin-bottom: 1em; }
.ballot-sidebar .position-group { margin-bottom: 1em; }
.ballot-sidebar .position-group .heading { font-weight: bold; }
@ -190,15 +200,14 @@ form table .help {
.ballot-sidebar .position-group .was { padding-left: 10px; font-size:85%; }
.ballot-sidebar .position-group:last-child { margin-bottom: 0; }
.ballot-content { margin-left: 180px; }
.ballot-content { margin-left: 180px; } /* this positions the content to the right of the sidebar */
.ballot-content .other-ballots { margin: 1em 0 2em 0; }
.ballot-content .other-ballots a { display: inline-block; margin-right: 0.5em; }
#ballot_dialog_body { background-color: #fff; }
#ballot_dialog_body .ballot-sidebar { margin-top: 0; }
.ballot-content h2.ad-ballot-comment { background: #2647A0; color: #fff; padding: 2px 4px; font-size: 108%; margin-top: 0;}
.ballot-popup > .content { width: 860px; height: 500px; overflow: auto; background: #fff; border: 1px solid #ccc; }
.ballot-popup .ballot-sidebar { margin-top: 0; }
ul.messages { border: solid black 1px; margin: 0.4em 0; padding: 1em; }
li.debug { margin: 0.5em; background-color: #ccf; }
li.info { margin: 0.5em; background-color: #ff8; }
@ -208,12 +217,17 @@ li.error { margin: 0.5em; background-color: #f44; }
.errorlist { background: red; color: white; padding: 0.2ex 0.2ex 0.2ex 0.5ex; border: 0px; margin: 0px; font-family: Arial, sans-serif; }
.group-documents .search-results { margin-top: 1.5em; }
table.milestones td.due { vertical-align: top; width: 80px; }
table.milestones .doc { display: block; padding-left: 1em; }
.stream-state .milestone { display: inline-block; font-size: smaller; background-color: #d5dde6; padding: 0 0.2em; margin-left: 0.3em; }
.button, .button:hover:disabled {
#modal-box { background: #f0f0f0; border-radius: 2px; padding: 10px; box-shadow: 0 0 4px rgba(0, 0, 0, 0.8); }
#modal-box .actions { padding-top: 1em; text-align: right; }
.button {
display: inline-block; padding: 4px 12px; margin-right: 0.3em;
color: #222; font-weight: normal; text-align: center; text-decoration: none; outline: none; cursor: pointer;
background: #eee; background: linear-gradient(#fff, #e0e0e0); background: -webkit-linear-gradient(#fff, #e0e0e0); background: -moz-linear-gradient(#fff, #e0e0e0);
@ -222,4 +236,4 @@ table.milestones .doc { display: block; padding-left: 1em; }
}
.button:hover { color: #111; background: #ddd; background: linear-gradient(#eee, #ccc); background: -webkit-linear-gradient(#eee, #ccc); background: -moz-linear-gradient(#eee, #ccc); }
.button:active { color: #000; background: #ccc; }
.button:disabled, .button:hover:disabled { color: #999; cursor: default; }
.button:disabled, .button:hover:disabled { color: #999; cursor: default; background: #eee; background: linear-gradient(#fff, #e0e0e0); background: -webkit-linear-gradient(#fff, #e0e0e0); background: -moz-linear-gradient(#fff, #e0e0e0); }

View file

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -4,7 +4,7 @@
<Description>Use datatracker.ietf.org to search for Internet-Drafts and RFCs</Description>
<Tags>Requests For Comments</Tags>
<Url type="text/html"
template="http://datatracker.ietf.org/doc/search/?name={searchTerms}&amp;activeDrafts=on&amp;rfcs=on&amp;lucky=on"/>
template="http://datatracker.ietf.org/doc/search/?name={searchTerms}&amp;activedrafts=on&amp;rfcs=on"/>
<LongName>datatracker.ietf.org RFC and Internet-Draft Search</LongName>
<Image height="16" width="16" type="image/vnd.microsoft.icon">http://datatracker.ietf.org/images/ietf-icon-blue.bmp</Image>
<Developer>Tony Hansen</Developer>

View file

@ -1,21 +1,23 @@
$(function () {
// search form
var form = jQuery("#search_form");
// we want to disable our submit button if we have no search text,
// and we have no advanced options selected
function toggleSubmit() {
var nameSearch = $.trim($("#id_name").val());
var noAdvanced = true;
function anyAdvancedActive() {
var advanced = false;
var by = form.find("input[name=by]:checked");
if (by.length > 0)
by.closest(".search_field").find("input,select").not("input[name=by]").each(function () {
if ($.trim(this.value))
noAdvanced = false;
advanced = true;
});
form.find("input[type=submit]").get(0).disabled = !nameSearch && noAdvanced;
return advanced;
}
function toggleSubmit() {
var nameSearch = $.trim($("#id_name").val());
form.find("input[type=submit]").get(0).disabled = !nameSearch && !anyAdvancedActive();
}
function togglePlusMinus(toggler, toggled) {
@ -29,7 +31,7 @@ $(function () {
}
}
function updateBy() {
function updateAdvanced() {
form.find("input[name=by]:checked").closest(".search_field").find("input,select").not("input[name=by]").each(function () {
this.disabled = false;
});
@ -41,25 +43,24 @@ $(function () {
toggleSubmit();
}
form.find(".search_field input[name=by]").closest("label").click(updateBy);
if (form.length > 0) {
form.find(".search_field input[name=by]").closest("label").click(updateAdvanced);
form.find(".search_field input,select")
.change(toggleSubmit).click(toggleSubmit).keyup(toggleSubmit);
form.find(".search_field input,select")
.change(toggleSubmit).click(toggleSubmit).keyup(toggleSubmit);
form.find(".toggle_advanced").click(function () {
var advanced = $(this).next();
advanced.find('.search_field input[type="radio"]').attr("checked", false);
togglePlusMinus($(this), advanced);
updateBy();
});
form.find(".toggle_advanced").click(function () {
var advanced = $(this).next();
advanced.find('.search_field input[type="radio"]').attr("checked", false);
togglePlusMinus($(this), advanced);
updateAdvanced();
});
updateBy();
updateAdvanced();
}
$("#search_results th").click(function (e) {
window.location = $(this).find("a").attr("href");
})
$('#search_results .addtolist a').click(function(e) {
// search results
$('.search-results .addtolist a').click(function(e) {
e.preventDefault();
var trigger = $(this);
$.ajax({
@ -74,4 +75,27 @@ $(function () {
}
});
});
$("a.ballot-icon").click(function (e) {
e.preventDefault();
$.ajax({
url: $(this).data("popup"),
success: function (data) {
showModalBox(data);
},
error: function () {
showModalBox("<div>Error retrieving popup content</div>");
}
});
}).each(function () {
// bind right-click shortcut
var editPositionUrl = $(this).data("edit");
if (editPositionUrl) {
$(this).bind("contextmenu", function (e) {
e.preventDefault();
window.location = editPositionUrl;
});
}
});
});

View file

@ -1,5 +1,5 @@
jQuery(function () {
jQuery("table.history .snippet .showAll").click(function () {
jQuery("table.history .snippet .show-all").click(function () {
jQuery(this).parents(".snippet").hide().siblings(".full").show();
});
});

43
static/js/utils.js Normal file
View file

@ -0,0 +1,43 @@
function showModalBox(content, callback) {
content = $(content);
// make sure previous one is gone
$("#modal-overlay").remove();
// the url(data:...) part is backwards compatibility for non-rgba
// supporting browsers (IE 8) - it's a 2 pixel black PNG with
// opacity 50%
var overlay = $('<div id="modal-overlay" style="position:fixed;z-index:100;top:0;left:0;height:100%;width:100%;background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAQAAABeK7cBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAADUlEQVQIHWNgqGeoBwACAQD/+EjsGgAAAABJRU5ErkJggg==);background:rgba(0,0,0,0.5);"></div>');
var box = $('<div id="modal-box" style="position:absolute;left:50%;top:50%"></div>');
box.append(content);
overlay.append(box);
box.click(function (e) {
e.stopPropagation();
});
overlay.click(closeModalBox);
box.find(".button.close").click(function (e) {
e.preventDefault();
closeModalBox();
});
overlay.keydown(function (e) {
if (e.which == 27)
closeModalBox();
});
$("body").append(overlay);
var w = content.outerWidth() || 400;
var h = content.outerHeight() || 300;
box.css({ "margin-left": -parseInt(w/2), "margin-top": -parseInt(h/2) });
content.focus();
if (callback)
callback();
}
function closeModalBox() {
$("#modal-overlay").remove();
}