Merged in a lot of shim-layer removals from olau@iola.dk

(-r5194:5465 from branch/iola/shimfree).  Copying relevant
commit messages here:

  - Removed .related many to many relationship, it's not really useful
    since we always have to restrict on the relationship type anyway,
    instead add two helpers for doing the necessary queries (in both
    directions)

  - Added migration for transforming the .desc on the new_revision events
    into something more akin to what is actually shown in the history page

  - Added migration for blanking IESG notes that just consist of "RFC
    XXXX", these have been superfluous for some time

  - Grant stream chairs access to changing the stream on a draft

  - Hacked the format_history_text filter to be less weird, using the same
    formatting for snippets and full text, also link up legacy ballot set
    events

  - Moved the decoraters + utilities to new ietfauth/utils.py file

  - Added simple helper to Email to identify invalid email addresses (from
    legacy author entries)

  - Used new new_revision .desc format for when drafts are submitted

  - Improved the looks of the button class by adding extra contrast and a
    linear gradient. Currently the gradient is only visible in fairly
    recent browsers.

  - Rewrote draft and RFC tabs in terms of the new schema, porting
    write-up and history tabs as well

  - Fixed two bugs in RFC Editor syncing: make sure documents we don't know
    beforehand get a "draft" type and make sure individually submitted
    drafts get the type="individ" group instead of NULL

  - Made the CSS-styled button feel a bit nicer to use by flattening the
    active state, also introduce some temporary styles until browsers
    catch up with the standard syntax

  - Added migrations for fixing 1) a dummy RFC entry, 2) three stand-alone
    RFCs that didn't get their doc.type set, 3) a big bunch of historic
    stand-alone RFCs that have doc.group=None - set these to the
    individual submission "none" group for the time being so the view code
    doesn't have to deal with a special case.

    In some cases this is wrong since there actually was a WG associated
    but unfortunately fixing them properly requires detective work
    (probably parsing the RFCs) and in at least some cases recreating
    historic WGs. In case someone ends up doing this, the documents to
    check can still be found with

    Document.objects.filter(name__startswith="rfc", group__type="individ")
    since there are almost no new RFCs that didn't went through the I-D
    process.

  - Merged the I-D and RFC views by showing I-D information on RFCs too.
    I-Ds that have been published as RFCs redirect to the RFC URL. Also
    support alias URLs so e.g. /doc/bcpXXXX redirects to /doc/rfcXXXX.

  - Fixed revision augmentation so events after RFC publication gets a "RFC"
    designation

  - Fixed a bug with tabs not using provided name but rather doc.name

  - Displaying draft-iesg state rather than doc.friendly_state as IESG state,
    also show a notice that the IESG state refers to post-RFC processing
    if it does, like the old separate RFC page did

  - Fixed the RFC number doc.note migration to catch combined "RFC XXX; BCP
    XXX" notes too, use the opportunity to remove inserted HTML tags from
    notes and rely on linebreaksbr filter instead (the other thing was a
    left-over from the Perl days), update the various uses of the note to
    reflect that

  - Refactored slightly to make views_doc.py independent of other idrfc code

  - Moveed idrfc/views_doc.py to doc/ with associated templates, replace the
    somewhat fragile simple URL tests for views_doc.py with ordinary unit
    tests. The new tests are still fairly basic but at least test more
    than the URL tests did.

  - Made sure RFC's (and BCP/STD/FYI) are stored as RFC123 instead of
    RFC0123 in the alias table with a new migration and a change to the
    RFC Editor sync, this in turn makes /doc/std1/ do the right thing

  - Now /doc/std1/ works, we can actually do a local link in
    urlize_ietf_docs rather than linking to the tools.ietf.org server

  - Fixed history text formatter: sanitize HTML before adding linebreaks and
    non-breaking spaces, this cuts the time to render a history page with
    long comments in half

  - Added a test crawler that walks through the crawlable part of the site,
    reporting errors and slow pages

  - Got rid of initial "No record" positions when showing old positions,
    it's just noise

  - Added a .select_related() to the document main tab to reduce the number
    of DB queries, unfortunately it seems it doesn't really help with
    Django 1.2.x due to a bug (Document inherits from DocumentInfo which
    makes things a bit more complicated)

  - Introduced a simple cache in doc.get_state so repeated reads don't
    cause a DB query

  - Cleaned up the search code in preparation for removal of the shim-layer;
    use a static button and don't send extraneous GET parameters

  - Removed dead code in several places
 - Legacy-Id: 5830
This commit is contained in:
Henrik Levkowetz 2013-07-15 20:55:24 +00:00
commit e678659b56
74 changed files with 4923 additions and 4452 deletions

View file

@ -494,6 +494,19 @@ class TransactionTestCase(unittest.TestCase):
def assertQuerysetEqual(self, qs, values, transform=repr):
return self.assertEqual(map(transform, qs), values)
# some things from newer (python 2.7) unittests
def assertIsNone(self, obj, msg=None):
"""Same as self.assertTrue(obj is None), with a nicer default message."""
if obj is not None:
standardMsg = '%s is not None' % (safe_repr(obj),)
self.fail(self._formatMessage(msg, standardMsg))
def assertIsNotNone(self, obj, msg=None):
"""Included for symmetry with assertIsNone."""
if obj is None:
standardMsg = 'unexpectedly None'
self.fail(self._formatMessage(msg, standardMsg))
def connections_support_transactions():
"""
Returns True if all connections support transactions. This is messy

91
ietf/bin/test-crawl Executable file
View file

@ -0,0 +1,91 @@
#!/usr/bin/env python
import os, sys, re, datetime, optparse, traceback
import syslog
# boilerplate
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
sys.path = [ basedir ] + sys.path
from ietf import settings
from django.core import management
management.setup_environ(settings)
import django.test
from django.conf import settings
# prevent memory from leaking when settings.DEBUG=True
from django.db import connection
class DontSaveQueries(object):
def append(self, x):
pass
connection.queries = DontSaveQueries()
MAX_URL_LENGTH = 500
SLOW_THRESHOLD = 1.0
def strip_url(url):
if url.startswith("http://testserver"):
url = url[len("http://testserver"):]
return url
def extract_html_urls(content):
for m in re.finditer(r'<a.*href="([^"]+)">', content):
url = strip_url(m.group(1))
if len(url) > MAX_URL_LENGTH:
continue # avoid infinite GET parameter appendages
if not url.startswith("/"):
continue
yield url
visited = set()
blacklist = set()
urls = set(["/doc/all/"])
client = django.test.Client()
while urls:
url = urls.pop()
visited.add(url)
try:
timestamp = datetime.datetime.now()
r = client.get(url)
elapsed = datetime.datetime.now() - timestamp
except KeyboardInterrupt:
print "was fetching", url
sys.exit(1)
except:
print "FAIL", url
print "============="
traceback.print_exc()
print "============="
else:
tags = []
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)
elif r.status_code == 200:
ctype = r["Content-Type"]
if ";" in ctype:
ctype = ctype[:ctype.index(";")]
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)
else:
tags.append("FAIL")
if elapsed.total_seconds() > SLOW_THRESHOLD:
tags.append("SLOW")
print r.status_code, "%.3fs" % elapsed.total_seconds(), url, " ".join(tags)

View file

@ -93,7 +93,7 @@ class DocumentAdmin(admin.ModelAdmin):
list_display = ['name', 'rev', 'group', 'pages', 'intended_std_level', 'author_list', 'time']
search_fields = ['name']
list_filter = ['type']
raw_id_fields = ['authors', 'related', 'group', 'shepherd', 'ad']
raw_id_fields = ['authors', 'group', 'shepherd', 'ad']
inlines = [DocAliasInline, DocAuthorInline, RelatedDocumentInline, ]
form = DocumentForm

View file

@ -0,0 +1,351 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
for e in orm.NewRevisionDocEvent.objects.filter(type="new_revision").iterator():
# fix the format
if (e.doc_id.startswith("draft-") or e.doc_id.startswith("charter-")) and e.desc in ("Added new revision", "New revision available"):
desc = "New version available: <b>%s-%s.txt</b>" % (e.doc_id, e.rev)
orm.DocEvent.objects.filter(id=e.docevent_ptr_id).update(desc=desc)
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.ballotdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']},
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
'doc.ballotpositiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['doc.BallotDocEvent']", 'null': 'True'}),
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"})
},
'doc.ballottype': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'doc.dochistory': {
'Meta': {'object_name': 'DocHistory'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.dochistoryauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {})
},
'doc.docreminder': {
'Meta': {'object_name': 'DocReminder'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'due': ('django.db.models.fields.DateTimeField', [], {}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'related': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'reversely_related_document_set'", 'blank': 'True', 'through': "orm['doc.RelatedDocument']", 'to': "orm['doc.DocAlias']"}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.initialreviewdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.lastcalldocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
'doc.relateddochistory': {
'Meta': {'object_name': 'RelatedDocHistory'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"})
},
'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'previous_states'", 'symmetrical': 'False', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'doc.telechatdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
},
'doc.writeupdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'name.ballotpositionname': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docrelationshipname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docremindertypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['doc']

View file

@ -0,0 +1,363 @@
# encoding: utf-8
import datetime, re
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
for d in orm.Document.objects.filter(type="draft").exclude(note="").iterator():
note = d.note
# get rid of HTML garbage
note = note.replace("\r", "")
note = note.replace("<br>", "\n")
note = note.replace("&nbsp;", " ")
note = note.strip()
# whack obsolete RFC/BCP/STD 1234 notes (obsolete now that
# we got RFC alias links set up properly),
# there are a couple that match without a
# corresponding RFC DocAlias, but upon manual
# inspection these all turn out to be mistakes in the
# note (most are off-by-one in the RFC number)
note = re.sub("^((STD|BCP|RFC)\s*#?\d+[\s,;]*)+", "", note).strip()
if note != d.note:
orm.Document.objects.filter(name=d.name).update(note=note)
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.ballotdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']},
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
'doc.ballotpositiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['doc.BallotDocEvent']", 'null': 'True'}),
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"})
},
'doc.ballottype': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'doc.dochistory': {
'Meta': {'object_name': 'DocHistory'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.dochistoryauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {})
},
'doc.docreminder': {
'Meta': {'object_name': 'DocReminder'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'due': ('django.db.models.fields.DateTimeField', [], {}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.initialreviewdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.lastcalldocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
'doc.relateddochistory': {
'Meta': {'object_name': 'RelatedDocHistory'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"})
},
'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'previous_states'", 'symmetrical': 'False', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'doc.telechatdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
},
'doc.writeupdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'name.ballotpositionname': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docrelationshipname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docremindertypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['doc']

View file

@ -0,0 +1,379 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
# first get rid of unconnected redundant RFCs
for d in orm.Document.objects.filter(name__startswith="rfc"):
if d.docalias_set.count() == 0 and orm.DocAlias.objects.filter(name=d.name):
d.delete() # this doc is unreachable
# fix up some standalone RFCs that didn't get type="draft" set
orm.Document.objects.filter(name__startswith="rfc", type=None).update(type="draft")
# fix RFCs with group=None, set them to the individual
# submitter "none" group - this may in some historic cases be
# wrong, but in many of those cases we don't have a group to
# connect the document to anyway (a later history fixing round
# can find these documents as
# Document.objects.filter(name__startswith="rfc", group__type="individ"))
orm.Document.objects.filter(name__startswith="rfc", group=None).update(group=orm["group.Group"].objects.get(type="individ"))
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.ballotdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']},
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
'doc.ballotpositiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['doc.BallotDocEvent']", 'null': 'True'}),
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"})
},
'doc.ballottype': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.consensusdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': ['doc.DocEvent']},
'consensus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
'doc.deletedevent': {
'Meta': {'object_name': 'DeletedEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'json': ('django.db.models.fields.TextField', [], {}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'doc.dochistory': {
'Meta': {'object_name': 'DocHistory'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.dochistoryauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {})
},
'doc.docreminder': {
'Meta': {'object_name': 'DocReminder'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'due': ('django.db.models.fields.DateTimeField', [], {}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.initialreviewdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.lastcalldocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
'doc.relateddochistory': {
'Meta': {'object_name': 'RelatedDocHistory'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"})
},
'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statedocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.State']", 'null': 'True', 'blank': 'True'}),
'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'doc.telechatdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
},
'doc.writeupdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'name.ballotpositionname': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docrelationshipname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docremindertypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['doc']

View file

@ -0,0 +1,369 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
for alias in orm.DocAlias.objects.filter(models.Q(name__startswith="bcp")
| models.Q(name__startswith="std")
| models.Q(name__startswith="fyi")):
# remove left-padded zeros
alias.name = alias.name[:3] + str(int(alias.name[3:]))
alias.save()
def backwards(self, orm):
"Write your backwards methods here."
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'doc.ballotdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']},
'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
'doc.ballotpositiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['doc.BallotDocEvent']", 'null': 'True'}),
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"})
},
'doc.ballottype': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.consensusdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': ['doc.DocEvent']},
'consensus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
},
'doc.deletedevent': {
'Meta': {'object_name': 'DeletedEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'json': ('django.db.models.fields.TextField', [], {}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
},
'doc.docalias': {
'Meta': {'object_name': 'DocAlias'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
'doc.docevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
'desc': ('django.db.models.fields.TextField', [], {}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'doc.dochistory': {
'Meta': {'object_name': 'DocHistory'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.dochistoryauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {})
},
'doc.docreminder': {
'Meta': {'object_name': 'DocReminder'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'due': ('django.db.models.fields.DateTimeField', [], {}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"})
},
'doc.document': {
'Meta': {'object_name': 'Document'},
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
},
'doc.documentauthor': {
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'doc.initialreviewdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.lastcalldocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'doc.newrevisiondocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
'doc.relateddochistory': {
'Meta': {'object_name': 'RelatedDocHistory'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"})
},
'doc.relateddocument': {
'Meta': {'object_name': 'RelatedDocument'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
},
'doc.state': {
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'doc.statedocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.State']", 'null': 'True', 'blank': 'True'}),
'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"})
},
'doc.statetype': {
'Meta': {'object_name': 'StateType'},
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
},
'doc.telechatdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
},
'doc.writeupdocevent': {
'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']},
'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'group.group': {
'Meta': {'object_name': 'Group'},
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
},
'name.ballotpositionname': {
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docrelationshipname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.docremindertypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctagname': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.doctypename': {
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.groupstatename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.grouptypename': {
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.intendedstdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.stdlevelname': {
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'name.streamname': {
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'person.person': {
'Meta': {'object_name': 'Person'},
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['doc']

View file

@ -4,6 +4,7 @@ from django.db import models
from django.core.urlresolvers import reverse as urlreverse
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django.utils.html import mark_safe
from ietf.group.models import *
from ietf.name.models import *
@ -99,20 +100,26 @@ class DocumentInfo(models.Model):
self.states.remove(*others)
if state not in already_set:
self.states.add(state)
self.state_cache = None # invalidate cache
def unset_state(self, state_type):
"""Unset state of type so no state of that type is any longer set."""
self.states.remove(*self.states.filter(type=state_type))
self.state_cache = None # invalidate cache
def get_state(self, state_type=None):
"""Get state of type, or default state for document type if not specified."""
"""Get state of type, or default state for document type if
not specified. Uses a local cache to speed multiple state
reads up."""
if state_type == None:
state_type = self.type_id
try:
return self.states.get(type=state_type)
except State.DoesNotExist:
return None
if not hasattr(self, "state_cache") or self.state_cache == None:
self.state_cache = {}
for s in self.states.all().select_related():
self.state_cache[s.type_id] = s
return self.state_cache.get(state_type, None)
def get_state_slug(self, state_type=None):
"""Get state of type, or default if not specified, returning
@ -137,8 +144,10 @@ class DocumentInfo(models.Model):
def active_ballot(self):
"""Returns the most recently created ballot if it isn't closed."""
ballot = self.latest_event(BallotDocEvent, type="created_ballot")
open = self.ballot_open(ballot.ballot_type.slug) if ballot else False
return ballot if open else None
if ballot and self.ballot_open(ballot.ballot_type.slug):
return ballot
else:
return None
class Meta:
abstract = True
@ -167,7 +176,7 @@ class DocumentAuthor(models.Model):
class Document(DocumentInfo):
name = models.CharField(max_length=255, primary_key=True) # immutable
related = models.ManyToManyField('DocAlias', through=RelatedDocument, blank=True, related_name="reversely_related_document_set")
#related = models.ManyToManyField('DocAlias', through=RelatedDocument, blank=True, related_name="reversely_related_document_set")
authors = models.ManyToManyField(Email, through=DocumentAuthor, blank=True)
def __unicode__(self):
@ -230,6 +239,14 @@ class Document(DocumentInfo):
name = name.upper()
return name
def related_that(self, relationship):
"""Return the documents that are source of relationship targeting self."""
return Document.objects.filter(relateddocument__target__document=self, relateddocument__relationship=relationship)
def related_that_doc(self, relationship):
"""Return the doc aliases that are target of relationship originating from self."""
return DocAlias.objects.filter(relateddocument__source=self, relateddocument__relationship=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):
@ -284,9 +301,6 @@ class Document(DocumentInfo):
qs = self.docalias_set.filter(name__startswith='rfc')
return qs[0].name[3:] if qs else None
def replaced_by(self):
return [ rel.source for alias in self.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='replaces') ]
def friendly_state(self):
""" Return a concise text description of the document's current state """
if self.type_id=='draft':
@ -305,12 +319,12 @@ class Document(DocumentInfo):
iesg_state_summary = iesg_state_summary + "::"+"::".join(tag.name for tag in iesg_substate)
if self.get_state_slug() == "rfc":
#return "<a href=\"%s\">RFC %d</a>" % (urlreverse('doc_view', args=['rfc%d' % int(self.rfc_number())]), int(self.rfc_number()))
return "RFC %d (%s)" % (int(self.rfc_number()), self.std_level)
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":
rs = self.replaced_by()
rs = self.related_that("replaces")
if rs:
return "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', args=[name]), name) for name in rs))
else:
return "Replaced"
elif self.get_state_slug() == "active":
@ -585,7 +599,13 @@ class BallotDocEvent(DocEvent):
if e.pos != prev:
latest.old_positions.append(e.pos)
# get rid of trailling "No record" positions, some old ballots
# have plenty of these
for p in positions:
while p.old_positions and p.old_positions[-1].slug == "norecord":
p.old_positions.pop()
# add any missing ADs through fake No Record events
if self.doc.active_ballot() == self:
norecord = BallotPositionName.objects.get(slug="norecord")

View file

@ -1,4 +1,144 @@
import os, shutil, datetime
import django.test
from django.core.urlresolvers import reverse as urlreverse
from django.conf import settings
from pyquery import PyQuery
from ietf.utils.mail import outbox
from ietf.utils.test_utils import login_testing_unauthorized
from ietf.utils.test_data import make_test_data
from ietf.doc.models import *
from ietf.name.models import *
from ietf.group.models import *
from ietf.person.models import *
from ietf.meeting.models import Meeting, MeetingTypeName
from ietf.iesg.models import TelechatDate
# extra tests
from ietf.doc.tests_conflict_review import *
from ietf.doc.tests_status_change import *
class DocTestCase(django.test.TestCase):
fixtures = ['names']
def test_document_draft(self):
draft = make_test_data()
# these tests aren't testing all attributes yet, feel free to
# expand them
# active draft
draft.set_state(State.objects.get(type="draft", slug="active"))
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=draft.name)))
self.assertEqual(r.status_code, 200)
self.assertTrue("Active Internet-Draft" in r.content)
# expired draft
draft.set_state(State.objects.get(type="draft", slug="expired"))
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=draft.name)))
self.assertEqual(r.status_code, 200)
self.assertTrue("Expired Internet-Draft" in r.content)
# replaced draft
draft.set_state(State.objects.get(type="draft", slug="repl"))
replacement = Document.objects.create(
name="draft-ietf-replacement",
time=datetime.datetime.now(),
type_id="draft",
title="Replacement Draft",
stream_id=draft.stream_id, group_id=draft.group_id, abstract=draft.stream, rev=draft.rev,
pages=draft.pages, intended_std_level_id=draft.intended_std_level_id,
shepherd_id=draft.shepherd_id, ad_id=draft.ad_id, expires=draft.expires,
notify=draft.notify, note=draft.note)
DocAlias.objects.create(name=replacement.name, document=replacement)
rel = RelatedDocument.objects.create(source=replacement,
target=draft.docalias_set.get(name__startswith="draft"),
relationship_id="replaces")
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=draft.name)))
self.assertEqual(r.status_code, 200)
self.assertTrue("Replaced Internet-Draft" in r.content)
self.assertTrue(replacement.name in r.content)
rel.delete()
# draft published as RFC
draft.set_state(State.objects.get(type="draft", slug="rfc"))
draft.std_level_id = "bcp"
draft.save()
DocEvent.objects.create(doc=draft, type="published_rfc", by=Person.objects.get(name="(System)"))
rfc_alias = DocAlias.objects.create(name="rfc123456", document=draft)
bcp_alias = DocAlias.objects.create(name="bcp123456", document=draft)
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=draft.name)))
self.assertEqual(r.status_code, 302)
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=bcp_alias.name)))
self.assertEqual(r.status_code, 302)
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=rfc_alias.name)))
self.assertEqual(r.status_code, 200)
self.assertTrue("RFC 123456" in r.content)
self.assertTrue(draft.name in r.content)
# naked RFC
rfc = Document.objects.create(
name="rfc1234567",
type_id="draft",
title="RFC without a Draft",
stream_id="ise",
group=Group.objects.get(type="individ"),
std_level_id="ps")
DocAlias.objects.create(name=rfc.name, document=rfc)
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=rfc.name)))
self.assertEqual(r.status_code, 200)
self.assertTrue("RFC 1234567" in r.content)
# unknown draft
r = self.client.get(urlreverse("doc_view", kwargs=dict(name="draft-xyz123")))
self.assertEqual(r.status_code, 404)
def test_document_charter(self):
make_test_data()
r = self.client.get(urlreverse("doc_view", kwargs=dict(name="charter-ietf-mars")))
self.assertEqual(r.status_code, 200)
def test_document_conflict_review(self):
make_test_data()
r = self.client.get(urlreverse("doc_view", kwargs=dict(name='conflict-review-imaginary-irtf-submission')))
self.assertEqual(r.status_code, 200)
def test_document_ballot(self):
doc = make_test_data()
ballot = doc.active_ballot()
BallotPositionDocEvent.objects.create(
doc=doc,
type="changed_ballot_position",
pos_id="yes",
comment="Looks fine to me",
comment_time=datetime.datetime.now(),
ad=Person.objects.get(user__username="ad"),
by=Person.objects.get(name="(System)"))
r = self.client.get(urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name)))
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)))
self.assertEqual(r.status_code, 200)
def test_document_json(self):
doc = make_test_data()
r = self.client.get(urlreverse("ietf.doc.views_doc.document_json", kwargs=dict(name=doc.name)))
self.assertEqual(r.status_code, 200)

View file

@ -1,4 +1,4 @@
import os
import os, re, urllib
import math
from django.conf import settings
@ -138,9 +138,12 @@ def augment_events_with_revision(doc, events):
event_revisions = list(NewRevisionDocEvent.objects.filter(doc=doc).order_by('time', 'id').values('id', 'rev', 'time'))
cur_rev = doc.rev
if doc.get_state_slug() == "rfc":
cur_rev = "RFC"
if doc.type_id == "draft" and doc.get_state_slug() == "rfc":
# add fake "RFC" revision
e = doc.latest_event(type="published_rfc")
if e:
event_revisions.append(dict(id=e.id, time=e.time, rev="RFC"))
event_revisions.sort(key=lambda x: (x["time"], x["id"]))
for e in sorted(events, key=lambda e: (e.time, e.id), reverse=True):
while event_revisions and (e.time, e.id) < (event_revisions[-1]["time"], event_revisions[-1]["id"]):
@ -153,6 +156,37 @@ def augment_events_with_revision(doc, events):
e.rev = cur_rev
def add_links_in_new_revision_events(doc, events, diff_revisions):
"""Add direct .txt links and diff links to new_revision events."""
prev = None
diff_urls = dict(((name, revision), url) for name, revision, time, url in diff_revisions)
for e in sorted(events, key=lambda e: (e.time, e.id)):
if not e.type == "new_revision":
continue
if not (e.doc.name, e.rev) in diff_urls:
continue
full_url = diff_url = diff_urls[(e.doc.name, e.rev)]
if doc.type_id in "draft": # work around special diff url for drafts
full_url = "http://tools.ietf.org/id/" + diff_url + ".txt"
# build links
links = r'<a href="%s">\1</a>' % full_url
if prev:
links += ""
if prev != None:
links += ' (<a href="http:%s?url1=%s&url2=%s">diff from previous</a>)' % (settings.RFCDIFF_PREFIX, urllib.quote(diff_url, safe="~"), urllib.quote(prev, safe="~"))
# replace the bold filename part
e.desc = re.sub(r"<b>(.+-[0-9][0-9].txt)</b>", links, e.desc)
prev = diff_url
def get_document_content(key, filename, split=True, markup=True):
f = None
@ -161,15 +195,12 @@ def get_document_content(key, filename, split=True, markup=True):
raw_content = f.read()
except IOError:
error = "Error; cannot read ("+key+")"
if split:
return (error, "")
else:
return error
return error
finally:
if f:
f.close()
if markup:
return markup_txt.markup(raw_content,split)
return markup_txt.markup(raw_content, split)
else:
return raw_content
@ -197,3 +228,16 @@ def add_state_change_event(doc, by, prev_state, new_state, timestamp=None):
e.save()
return e
def prettify_std_name(n):
if re.match(r"(rfc|bcp|fyi|std)[0-9]+", n):
return n[:3].upper() + " " + n[3:]
else:
return n
def nice_consensus(consensus):
mapping = {
None: "Unknown",
True: "Yes",
False: "No"
}
return mapping[consensus]

701
ietf/doc/views_doc.py Normal file
View file

@ -0,0 +1,701 @@
# 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.
import re, os, datetime, urllib
from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.template.loader import render_to_string
from django.template.defaultfilters import truncatewords_html
from django.utils import simplejson as json
from django.utils.decorators import decorator_from_middleware
from django.middleware.gzip import GZipMiddleware
from django.core.urlresolvers import reverse as urlreverse, NoReverseMatch
from django.conf import settings
from ietf.doc.models import *
from ietf.doc.utils import *
from ietf.utils.history import find_history_active_at
from ietf.ietfauth.utils import *
def render_document_top(request, doc, tab, name):
tabs = []
tabs.append(("Document", "document", urlreverse("doc_view", kwargs=dict(name=name)), True))
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
if doc.type_id in ("draft","conflrev"):
# if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
tabs.append(("IESG Evaluation Record", "ballot", urlreverse("doc_ballot", kwargs=dict(name=name)), ballot))
elif doc.type_id == "charter":
tabs.append(("IESG Review", "ballot", urlreverse("doc_ballot", kwargs=dict(name=name)), ballot))
# FIXME: if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
if doc.type_id != "conflrev":
tabs.append(("IESG Writeups", "writeup", urlreverse("doc_writeup", kwargs=dict(name=name)), True))
tabs.append(("History", "history", urlreverse("doc_history", kwargs=dict(name=name)), True))
if name.startswith("rfc"):
name = "RFC %s" % name[3:]
else:
name += "-" + doc.rev
return render_to_string("doc/document_top.html",
dict(doc=doc,
tabs=tabs,
selected=tab,
name=name))
@decorator_from_middleware(GZipMiddleware)
def document_main(request, name, rev=None):
doc = get_object_or_404(Document.objects.select_related(), docalias__name=name)
# take care of possible redirections
aliases = DocAlias.objects.filter(document=doc).values_list("name", flat=True)
if doc.type_id == "draft" and not name.startswith("rfc"):
for a in aliases:
if a.startswith("rfc"):
return redirect("doc_view", name=a)
group = doc.group
if doc.type_id == 'conflrev':
conflictdoc = doc.related_that_doc('conflrev').get().document
revisions = []
for h in doc.history_set.order_by("time", "id"):
if h.rev and not h.rev in revisions:
revisions.append(h.rev)
if not doc.rev in revisions:
revisions.append(doc.rev)
snapshot = False
if rev != None:
if rev == doc.rev:
return redirect('doc_view', name=name)
# find the entry in the history
for h in doc.history_set.order_by("-time"):
if rev == h.rev:
snapshot = True
doc = h
break
if not snapshot:
return redirect('doc_view', name=name)
if doc.type_id == "charter":
# find old group, too
gh = find_history_active_at(doc.group, doc.time)
if gh:
group = gh
top = render_document_top(request, doc, "document", name)
telechat = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
if telechat and (not telechat.telechat_date or telechat.telechat_date < datetime.date.today()):
telechat = None
# specific document types
if doc.type_id == "draft":
split_content = not request.GET.get('include_text')
if request.COOKIES.get("full_draft", "") == "on":
split = False
iesg_state = doc.get_state("draft-iesg")
can_edit = has_role(request.user, ("Area Director", "Secretariat"))
stream_slugs = StreamName.objects.values_list("slug", flat=True)
can_change_stream = bool(can_edit or (
request.user.is_authenticated() and
Role.objects.filter(name__in=("chair", "auth"),
group__acronym__in=stream_slugs,
person__user=request.user)))
can_edit_iana_state = has_role(request.user, ("Secretariat", "IANA"))
rfc_number = name[3:] if name.startswith("") else None
draft_name = None
for a in aliases:
if a.startswith("draft"):
draft_name = a
rfc_aliases = [prettify_std_name(a) for a in aliases
if a.startswith("fyi") or a.startswith("std") or a.startswith("bcp")]
latest_revision = None
if doc.get_state_slug() == "rfc":
# content
filename = name + ".txt"
content = get_document_content(filename, os.path.join(settings.RFC_PATH, filename),
split_content, markup=True)
# file types
base_path = os.path.join(settings.RFC_PATH, name + ".")
possible_types = ["txt", "pdf", "ps"]
found_types = [t for t in possible_types if os.path.exists(base_path + t)]
base = "http://www.rfc-editor.org/rfc/"
file_urls = []
for t in found_types:
label = "plain text" if t == "txt" else t
file_urls.append((label, base + name + "." + t))
if "pdf" not in found_types and "txt" in found_types:
file_urls.append(("pdf", base + "pdfrfc/" + name + ".txt.pdf"))
if "txt" in found_types:
file_urls.append(("html", "http://tools.ietf.org/html/" + name))
if not found_types:
content = "This RFC is not currently available online."
split_content = False
elif "txt" not in found_types:
content = "This RFC is not available in plain text format."
split_content = False
else:
filename = "%s-%s.txt" % (draft_name, doc.rev)
content = get_document_content(filename, os.path.join(settings.INTERNET_DRAFT_PATH, filename),
split_content, markup=True)
# file types
base_path = os.path.join(settings.INTERNET_DRAFT_PATH, doc.name + "-" + doc.rev + ".")
possible_types = ["pdf", "xml", "ps"]
found_types = ["txt"] + [t for t in possible_types if os.path.exists(base_path + t)]
tools_base = "http://tools.ietf.org/"
if doc.get_state_slug() == "active":
base = "http://www.ietf.org/id/"
else:
base = tools_base + "id/"
file_urls = []
for t in found_types:
label = "plain text" if t == "txt" else t
file_urls.append((label, base + doc.name + "-" + doc.rev + "." + t))
if "pdf" not in found_types:
file_urls.append(("pdf", tools_base + "pdf/" + doc.name + "-" + doc.rev + ".pdf"))
file_urls.append(("html", tools_base + "html/" + doc.name + "-" + doc.rev))
# latest revision
latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision")
# ballot
ballot_summary = None
if iesg_state and iesg_state.slug in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"):
active_ballot = doc.active_ballot()
if active_ballot:
ballot_summary = needed_ballot_positions(doc, active_ballot.active_ad_positions().values())
# submission
submission = ""
if group.type_id == "individ":
submission = "individual"
elif group.type_id == "area" and doc.stream_id == "ietf":
submission = "individual in %s area" % group.acronym
elif group.type_id in ("rg", "wg"):
submission = "%s %s" % (group.acronym, group.type)
if group.type_id == "wg":
submission = "<a href=\"%s\">%s</a>" % (urlreverse("wg_docs", kwargs=dict(acronym=doc.group.acronym)), submission)
if doc.stream_id and doc.get_state_slug("draft-stream-%s" % doc.stream_id) == "c-adopt":
submission = "candidate for %s" % submission
# resurrection
resurrected_by = None
if doc.get_state_slug() == "expired":
e = doc.latest_event(type__in=("requested_resurrect", "completed_resurrect"))
if e and e.type == "requested_resurrect":
resurrected_by = e.by
# stream info
stream_state = None
if doc.stream:
stream_state = doc.get_state("draft-stream-%s" % doc.stream_id)
stream_tags = doc.tags.filter(slug__in=get_tags_for_stream_id(doc.stream_id))
shepherd_writeup = doc.latest_event(WriteupDocEvent, type="changed_protocol_writeup")
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
can_edit_shepherd_writeup = can_edit_stream_info or user_is_person(request.user, doc.shepherd) or has_role(request.user, ["Area Director"])
consensus = None
if doc.stream_id in ("ietf", "irtf", "iab"):
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
consensus = nice_consensus(e and e.consensus)
# mailing list search archive
search_archive = "www.ietf.org/mail-archive/web/"
if doc.stream_id == "ietf" and group.type_id == "wg" and group.list_archive:
search_archive = group.list_archive
search_archive = urllib.quote(search_archive, safe="~")
# conflict reviews
conflict_reviews = [d.name for d in doc.related_that("conflrev")]
# remaining actions
actions = []
if ((not doc.stream_id or doc.stream_id in ("ietf", "irtf")) and group.type_id == "individ" and
(request.user.is_authenticated() and
Role.objects.filter(person__user=request.user, name__in=("chair", "delegate"),
group__type__in=("wg",),
group__state="active")
or has_role(request.user, "Secretariat"))):
actions.append(("Adopt in Group", urlreverse('edit_adopt', kwargs=dict(name=doc.name))))
if doc.get_state_slug() == "expired" and not resurrected_by and can_edit:
actions.append(("Request Resurrect", urlreverse('doc_request_resurrect', kwargs=dict(name=doc.name))))
if doc.get_state_slug() == "expired" and has_role(request.user, ("Secretariat",)):
actions.append(("Resurrect", urlreverse('doc_resurrect', kwargs=dict(name=doc.name))))
if (doc.get_state_slug() != "expired" and doc.stream_id in ("ise", "irtf")
and has_role(request.user, ("Secretariat",)) and not conflict_reviews):
label = "Begin IETF Conflict Review"
if not doc.intended_std_level:
label += " (note that intended status is not set)"
actions.append((label, urlreverse('conflict_review_start', kwargs=dict(name=doc.name))))
if doc.get_state_slug() != "expired" and doc.stream_id in ("ietf",) and can_edit and not iesg_state:
actions.append(("Begin IESG Processing", urlreverse('doc_edit_info', kwargs=dict(name=doc.name)) + "?new=1"))
return render_to_response("doc/document_draft.html",
dict(doc=doc,
group=group,
top=top,
name=name,
content=content,
split_content=split_content,
revisions=revisions,
snapshot=snapshot,
latest_revision=latest_revision,
can_edit=can_edit,
can_change_stream=can_change_stream,
can_edit_stream_info=can_edit_stream_info,
can_edit_shepherd_writeup=can_edit_shepherd_writeup,
can_edit_iana_state=can_edit_iana_state,
rfc_number=rfc_number,
draft_name=draft_name,
telechat=telechat,
ballot_summary=ballot_summary,
submission=submission,
resurrected_by=resurrected_by,
replaces=[d.name for d in doc.related_that_doc("replaces")],
replaced_by=[d.name for d in doc.related_that("replaces")],
updates=[prettify_std_name(d.name) for d in doc.related_that_doc("updates")],
updated_by=[prettify_std_name(d.canonical_name()) for d in doc.related_that("updates")],
obsoletes=[prettify_std_name(d.name) for d in doc.related_that_doc("obs")],
obsoleted_by=[prettify_std_name(d.canonical_name()) for d in doc.related_that("obs")],
conflict_reviews=conflict_reviews,
rfc_aliases=rfc_aliases,
has_errata=doc.tags.filter(slug="errata"),
published=doc.latest_event(type="published_rfc"),
file_urls=file_urls,
stream_state=stream_state,
stream_tags=stream_tags,
milestones=doc.groupmilestone_set.filter(state="active"),
consensus=consensus,
iesg_state=iesg_state,
rfc_editor_state=doc.get_state("draft-rfceditor"),
iana_review_state=doc.get_state("draft-iana-review"),
iana_action_state=doc.get_state("draft-iana-action"),
started_iesg_process=doc.latest_event(type="started_iesg_process"),
shepherd_writeup=shepherd_writeup,
search_archive=search_archive,
actions=actions,
),
context_instance=RequestContext(request))
if doc.type_id == "charter":
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
content = get_document_content(filename, os.path.join(settings.CHARTER_PATH, filename), split=False, markup=True)
ballot_summary = None
if doc.get_state_slug() in ("intrev", "iesgrev"):
active_ballot = doc.active_ballot()
if active_ballot:
ballot_summary = needed_ballot_positions(doc, active_ballot.active_ad_positions().values())
else:
ballot_summary = "No active ballot found."
chartering = get_chartering_type(doc)
# inject milestones from group
milestones = None
if chartering and not snapshot:
milestones = doc.group.groupmilestone_set.filter(state="charter")
return render_to_response("doc/document_charter.html",
dict(doc=doc,
top=top,
chartering=chartering,
content=content,
txt_url=settings.CHARTER_TXT_URL + filename,
revisions=revisions,
snapshot=snapshot,
telechat=telechat,
ballot_summary=ballot_summary,
group=group,
milestones=milestones,
),
context_instance=RequestContext(request))
if doc.type_id == "conflrev":
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
pathname = os.path.join(settings.CONFLICT_REVIEW_PATH,filename)
if doc.rev == "00" and not os.path.isfile(pathname):
# This could move to a template
content = "A conflict review response has not yet been proposed."
else:
content = get_document_content(filename, pathname, split=False, markup=True)
ballot_summary = None
if doc.get_state_slug() in ("iesgeval"):
ballot_summary = needed_ballot_positions(doc, doc.active_ballot().active_ad_positions().values())
return render_to_response("doc/document_conflict_review.html",
dict(doc=doc,
top=top,
content=content,
revisions=revisions,
snapshot=snapshot,
telechat=telechat,
conflictdoc=conflictdoc,
ballot_summary=ballot_summary,
approved_states=('appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent')
),
context_instance=RequestContext(request))
raise Http404
def document_history(request, name):
doc = get_object_or_404(Document, docalias__name=name)
top = render_document_top(request, doc, "history", name)
# pick up revisions from events
diff_revisions = []
diffable = name.startswith("draft") or name.startswith("charter") or name.startswith("conflict-review")
if diffable:
diff_documents = [ doc ]
diff_documents.extend(Document.objects.filter(docalias__relateddocument__source=doc, docalias__relateddocument__relationship="replaces"))
seen = set()
for e in NewRevisionDocEvent.objects.filter(type="new_revision", doc__in=diff_documents).select_related('doc').order_by("-time", "-id"):
if (e.doc.name, e.rev) in seen:
continue
seen.add((e.doc.name, e.rev))
url = ""
if name.startswith("charter"):
url = request.build_absolute_uri(urlreverse("charter_with_milestones_txt", kwargs=dict(name=e.doc.name, rev=e.rev)))
elif name.startswith("conflict-review"):
h = find_history_active_at(e.doc, e.time)
url = settings.CONFLICT_REVIEW_TXT_URL + ("%s-%s.txt" % ((h or doc).canonical_name(), e.rev))
elif name.startswith("draft"):
# rfcdiff tool has special support for IDs
url = e.doc.name + "-" + e.rev
diff_revisions.append((e.doc.name, e.rev, e.time, url))
# grab event history
events = doc.docevent_set.all().order_by("-time", "-id").select_related("by")
augment_events_with_revision(doc, events)
add_links_in_new_revision_events(doc, events, diff_revisions)
return render_to_response("doc/document_history.html",
dict(doc=doc,
top=top,
diff_revisions=diff_revisions,
events=events,
),
context_instance=RequestContext(request))
def document_writeup(request, name):
doc = get_object_or_404(Document, docalias__name=name)
top = render_document_top(request, doc, "writeup", name)
def text_from_writeup(event_type):
e = doc.latest_event(WriteupDocEvent, type=event_type)
if e:
return e.text
else:
return ""
sections = []
if doc.type_id == "draft":
writeups = []
sections.append(("Approval Announcement",
"<em>Draft</em> of message to be sent <em>after</em> approval:",
writeups))
writeups.append(("Announcement",
text_from_writeup("changed_ballot_approval_text"),
urlreverse("doc_ballot_approvaltext", kwargs=dict(name=doc.name))))
writeups.append(("Ballot Text",
text_from_writeup("changed_ballot_writeup_text"),
urlreverse("doc_ballot_writeupnotes", kwargs=dict(name=doc.name))))
elif doc.type_id == "charter":
sections.append(("WG Review Announcement",
"",
[("WG Review Announcement",
text_from_writeup("changed_review_announcement"),
urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="review")))]
))
sections.append(("WG Action Announcement",
"",
[("WG Action Announcement",
text_from_writeup("changed_action_announcement"),
urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="action")))]
))
if doc.latest_event(BallotDocEvent, type="created_ballot"):
sections.append(("Ballot Announcement",
"",
[("Ballot Announcement",
text_from_writeup("changed_ballot_writeup_text"),
urlreverse("ietf.wgcharter.views.ballot_writeupnotes", kwargs=dict(name=doc.name)))]
))
if not sections:
raise Http404()
return render_to_response("doc/document_writeup.html",
dict(doc=doc,
top=top,
sections=sections,
can_edit=has_role(request.user, ("Area Director", "Secretariat")),
),
context_instance=RequestContext(request))
def document_shepherd_writeup(request, name):
doc = get_object_or_404(Document, docalias__name=name)
lastwriteup = doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup")
if lastwriteup:
writeup_text = lastwriteup.text
else:
writeup_text = "(There is no shepherd's writeup available for this document)"
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
can_edit_shepherd_writeup = can_edit_stream_info or user_is_person(request.user, doc.shepherd) or has_role(request.user, ["Area Director"])
return render_to_response("doc/shepherd_writeup.html",
dict(doc=doc,
writeup=writeup_text,
can_edit=can_edit_shepherd_writeup
),
context_instance=RequestContext(request))
def document_ballot_content(request, doc, ballot_id, editable=True):
"""Render HTML string with content of ballot page."""
all_ballots = list(BallotDocEvent.objects.filter(doc=doc, type="created_ballot").order_by("time"))
augment_events_with_revision(doc, all_ballots)
ballot = None
if ballot_id != None:
ballot_id = int(ballot_id)
for b in all_ballots:
if b.id == ballot_id:
ballot = b
break
elif all_ballots:
ballot = all_ballots[-1]
if not ballot:
raise Http404
deferred = doc.active_defer_event()
positions = ballot.all_positions()
# put into position groups
position_groups = []
for n in BallotPositionName.objects.filter(slug__in=[p.pos_id for p in positions]).order_by('order'):
g = (n, [p for p in positions if p.pos_id == n.slug])
g[1].sort(key=lambda p: (p.old_ad, p.ad.plain_name()))
if n.blocking:
position_groups.insert(0, g)
else:
position_groups.append(g)
summary = needed_ballot_positions(doc, [p for p in positions if not p.old_ad])
text_positions = [p for p in positions if p.discuss or p.comment]
text_positions.sort(key=lambda p: (p.old_ad, p.ad.plain_name()))
ballot_open = not BallotDocEvent.objects.filter(doc=doc,
type__in=("closed_ballot", "created_ballot"),
time__gt=ballot.time,
ballot_type=ballot.ballot_type)
if not ballot_open:
editable = False
return render_to_string("doc/document_ballot_content.html",
dict(doc=doc,
ballot=ballot,
position_groups=position_groups,
text_positions=text_positions,
editable=editable,
ballot_open=ballot_open,
deferred=deferred,
summary=summary,
all_ballots=all_ballots,
),
context_instance=RequestContext(request))
def document_ballot(request, name, ballot_id=None):
doc = get_object_or_404(Document, docalias__name=name)
top = render_document_top(request, doc, "ballot", name)
c = document_ballot_content(request, doc, ballot_id, editable=True)
return render_to_response("doc/document_ballot.html",
dict(doc=doc,
top=top,
ballot_content=c,
),
context_instance=RequestContext(request))
def document_json(request, name):
doc = get_object_or_404(Document, docalias__name=name)
def extract_name(s):
return s.name if s else None
data = {}
data["name"] = doc.name
data["rev"] = doc.rev
data["time"] = doc.time.strftime("%Y-%m-%d %H:%M:%S")
data["group"] = None
if doc.group:
data["group"] = dict(
name=doc.group.name,
type=extract_name(doc.group.type),
acronym=doc.group.acronym)
data["expires"] = doc.expires.strftime("%Y-%m-%d %H:%M:%S") if doc.expires else None
data["title"] = doc.title
data["abstract"] = doc.abstract
data["aliases"] = list(doc.docalias_set.values_list("name", flat=True))
data["state"] = extract_name(doc.get_state())
data["intended_std_level"] = extract_name(doc.intended_std_level)
data["std_level"] = extract_name(doc.std_level)
data["authors"] = [
dict(name=e.person.name,
email=e.address,
affiliation=e.person.affiliation)
for e in Email.objects.filter(documentauthor__document=doc).select_related("person").order_by("documentauthor__order")
]
data["shepherd"] = doc.shepherd.formatted_email() if doc.shepherd else None
data["ad"] = doc.ad.role_email("ad").formatted_email() if doc.ad else None
if doc.type_id == "draft":
data["iesg_state"] = extract_name(doc.get_state("draft-iesg"))
data["rfceditor_state"] = extract_name(doc.get_state("draft-rfceditor"))
data["iana_review_state"] = extract_name(doc.get_state("draft-iana-review"))
data["iana_action_state"] = extract_name(doc.get_state("draft-iana-action"))
if doc.stream_id in ("ietf", "irtf", "iab"):
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
data["consensus"] = e.consensus if e else None
data["stream"] = extract_name(doc.stream)
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):
from ietf.doc.models import DocAlias
alias = get_object_or_404(DocAlias, name=name)
d = alias.document
from ietf.idtracker.models import InternetDraft, BallotInfo
from ietf.idrfc.idrfc_wrapper import BallotWrapper, IdWrapper, RfcWrapper
id = None
bw = None
dw = None
if (d.type_id=='draft'):
id = get_object_or_404(InternetDraft, name=d.name)
try:
if not id.ballot.ballot_issued:
raise Http404
except BallotInfo.DoesNotExist:
raise Http404
bw = BallotWrapper(id) # XXX Fixme: Eliminate this as we go forward
# Python caches ~100 regex'es -- explicitly compiling it inside a method
# (where you then throw away the compiled version!) doesn't make sense at
# all.
if re.search("^rfc([1-9][0-9]*)$", name):
id.viewing_as_rfc = True
dw = RfcWrapper(id)
else:
dw = IdWrapper(id)
# XXX Fixme: Eliminate 'dw' as we go forward
try:
b = d.latest_event(BallotDocEvent, type="created_ballot")
except BallotDocEvent.DoesNotExist:
raise Http404
return (bw, dw, b, d)
ballot, doc, b, d = get_ballot(name)
response = HttpResponse(mimetype='text/plain')
response.write(json.dumps(ballot.dict(), indent=2))
return response

View file

@ -395,7 +395,7 @@ class RfcWrapper:
@models.permalink
def get_absolute_url(self):
return ('ietf.idrfc.views_doc.document_main', ['rfc%s' % (str(self.rfc_number))])
return ('ietf.doc.views_doc.document_main', ['rfc%s' % (str(self.rfc_number))])
def displayname_with_link(self):
return '<a href="%s">RFC %d</a>' % (self.get_absolute_url(), self.rfc_number)

View file

@ -66,7 +66,8 @@ def markup(content, split=True):
if split:
n = content.find("\n", 5000)
content1 = "<pre>"+content[:n+1]+"</pre>\n"
content2 = "<pre>"+content[n+1:]+"</pre>\n"
return (content1, content2)
return content1
#content2 = "<pre>"+content[n+1:]+"</pre>\n"
#return (content1, content2)
else:
return "<pre>" + content + "</pre>\n"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,88 +0,0 @@
200 /
200 /doc/
200,heavy /doc/all/
200,heavy /doc/active/
# draft that's now RFC
200 /doc/draft-ietf-avt-rtp-atrac-family/
200 /doc/draft-ietf-avt-rtp-atrac-family/doc.json
200 /doc/draft-ietf-avt-rtp-atrac-family/ballot.json
200 /doc/draft-ietf-avt-rtp-atrac-family/_ballot.data
# replaced draft, never went to IESG
200 /doc/draft-eronen-mobike-mopo/
404 /doc/draft-eronen-mobike-mopo/ballot.json
404 /doc/draft-eronen-mobike-mopo/_ballot.data
# expired draft
200 /doc/draft-eronen-eap-sim-aka-80211/
# Normal RFC
200 /doc/rfc4739/
200 /doc/rfc4739/doc.json
404 /doc/rfc4739/ballot.json
404 /doc/rfc4739/_ballot.data
# RFC that's evaluated in IESG
200 /doc/rfc3852/
200 /doc/rfc3852/doc.json
200 /doc/rfc3852/ballot.json
200 /doc/rfc3852/_ballot.data
# old RFC
200 /doc/rfc822/
200 /doc/rfc822/doc.json
# ballot sets
200 /doc/rfc3550/ballot.json
200 /doc/rfc3550/_ballot.data
200 /doc/rfc3551/ballot.json
200 /doc/rfc3551/_ballot.data
200 /doc/draft-irtf-dtnrg-ltp/ballot.json
200 /doc/draft-irtf-dtnrg-ltp/_ballot.data
# file formats
200 /doc/rfc9/ # PDF only
200 /doc/rfc2490/ # TXT+PDF+PS
200 /doc/rfc500/ # not online
404 /doc/draft-no-such-draft/
404 /doc/rfc4637/
200 /doc/rfc2444/doc.json # foreignkey problem with Django 1.x
# current AD -- needs to be updated at some point
200 /doc/ad/robert.sparks/
# former AD
200 /doc/ad/sam.hartman/
404 /doc/ad/no.body/
# ballot exists, but it's not issued
404 /doc/draft-ietf-aaa-diameter-api/ballot.json
404 /doc/draft-ietf-aaa-diameter-api/_ballot.data
# ballot does not exist
404 /doc/draft-zeilenga-cldap/ballot.json
404 /doc/draft-zeilenga-cldap/_ballot.data
# comment with created_by=999
200 /doc/draft-ietf-l3vpn-2547bis-mcast-bgp/
# comment with created_by=0 (and no idinternal entry)
200 /doc/draft-ietf-proto-wgdocument-states/
200 /doc/search/
200 /doc/search/?rfcs=on&name=snmp
200 /doc/search/?rfcs=on&name=nfs&by=ad&ad=53
200 /doc/search/?activeDrafts=on&name=sipping
200 /doc/search/?oldDrafts=on&name=tls
200 /doc/search/?activeDrafts=on&oldDrafts=on&ad=53&by=ad
200 /doc/search/?activeDrafts=on&state=20&by=state
200 /doc/search/?activeDrafts=on&oldDrafts=on&subState=5&by=state
200 /doc/search/?activeDrafts=on&oldDrafts=on&rfcs=on&ad=53&name=nfs&by=ad
200 /doc/search/?rfcs=on&group=tls&by=group
200 /doc/search/?activeDrafts=on&group=tls&by=group
200 /doc/search/?activeDrafts=on&oldDrafts=on&rfcs=on&author=eronen&by=author
200 /doc/search/?activeDrafts=on&oldDrafts=on&rfcs=on&area=934&name=ldap&by=area
200 /doc/search/?activeDrafts=on&name=asdfsadfsdfasdf
200 /doc/search/?activeDrafts=on&name=%EF%BD%8C #non-ASCII
# Test case for missing publication date
200 /doc/search/?oldDrafts=on&name=ppvpn

View file

@ -3,71 +3,12 @@
200,heavy /doc/all/
200,heavy /doc/active/
# draft that's now RFC
200 /doc/draft-ietf-avt-rtp-atrac-family/
200 /doc/draft-ietf-avt-rtp-atrac-family/doc.json
200 /doc/draft-ietf-avt-rtp-atrac-family/ballot.json
200 /doc/draft-ietf-avt-rtp-atrac-family/ballotpopup/
# replaced draft, never went to IESG
200 /doc/draft-eronen-mobike-mopo/
404 /doc/draft-eronen-mobike-mopo/ballot.json
404 /doc/draft-eronen-mobike-mopo/ballotpopup/
# expired draft
200 /doc/draft-eronen-eap-sim-aka-80211/
# Normal RFC
200 /doc/rfc4739/
200 /doc/rfc4739/doc.json
200 /doc/rfc4739/ballot.json # has ballot from I-D
200 /doc/rfc4739/ballotpopup/
# RFC that's evaluated in IESG
200 /doc/rfc3852/
200 /doc/rfc3852/doc.json
200 /doc/rfc3852/ballot.json
200 /doc/rfc3852/ballotpopup/
# old RFC
200 /doc/rfc822/
200 /doc/rfc822/doc.json
# ballot sets
200 /doc/rfc3550/ballot.json
200 /doc/rfc3550/ballotpopup/
200 /doc/rfc3551/ballot.json
200 /doc/rfc3551/ballotpopup/
200 /doc/draft-irtf-dtnrg-ltp/ballot.json
200 /doc/draft-irtf-dtnrg-ltp/ballotpopup/
# file formats
200 /doc/rfc9/ # PDF only
200 /doc/rfc2490/ # TXT+PDF+PS
200 /doc/rfc500/ # not online
404 /doc/draft-no-such-draft/
404 /doc/rfc4637/
200 /doc/rfc2444/doc.json # foreignkey problem with Django 1.x
# current AD -- needs to be updated at some point
200 /doc/ad/robert.sparks/
# former AD
200 /doc/ad/sam.hartman/
404 /doc/ad/no.body/
# ballot exists, but it's not issued
404 /doc/draft-ietf-aaa-diameter-api/ballot.json
404 /doc/draft-ietf-aaa-diameter-api/ballotpopup/
# ballot does not exist
404 /doc/draft-zeilenga-cldap/ballot.json
404 /doc/draft-zeilenga-cldap/ballotpopup/
# comment with created_by=999
200 /doc/draft-ietf-l3vpn-2547bis-mcast-bgp/
# comment with created_by=0 (and no idinternal entry)
200 /doc/draft-ietf-proto-wgdocument-states/
200 /doc/search/
200 /doc/search/?rfcs=on&name=snmp
200 /doc/search/?rfcs=on&name=nfs&by=ad&ad=112773

View file

@ -31,9 +31,10 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from django.conf.urls.defaults import patterns, url, include
from ietf.idrfc import views_doc, views_search, views_edit, views_ballot, views
from ietf.idrfc import views_search, views_edit, views_ballot, views
from ietf.doc.models import State
from ietf.doc import views_status_change
from ietf.doc import views_doc
urlpatterns = patterns('',
(r'^/?$', views_search.search_main),
@ -56,8 +57,7 @@ urlpatterns = patterns('',
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._+-]+)/ballot.tsv$', views_doc.ballot_tsv),
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot.json$', views_doc.ballot_json),
#(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
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/state/(?P<state_type>iana-action|iana-review)/$', views_edit.change_iana_state, name='doc_change_iana_state'),

View file

@ -2,7 +2,7 @@ from django.conf import settings
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
from ietf.idrfc.mails import *
from ietf.ietfauth.decorators import has_role
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream
def add_document_comment(request, doc, text, ballot=None):
if request:
@ -177,27 +177,3 @@ def update_telechatREDESIGN(request, doc, by, new_telechat_date, new_returning_i
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
update_telechat = update_telechatREDESIGN
def can_edit_base(doc, user):
return user.is_authenticated() and (
has_role(user, ["Secretariat", "Area Director"]) or
doc.group.role_set.filter(name__in=("chair", "auth", "delegate"), person__user=user)
)
can_edit_intended_std_level = can_edit_base
can_edit_consensus = can_edit_base
can_edit_shepherd = can_edit_base
def can_edit_shepherd_writeup(doc, user):
if user.is_authenticated():
if Person.objects.filter(user=user).count():
return can_edit_base(doc,user) or (doc.shepherd==user.person)
return False
def nice_consensus(consensus):
mapping = {
None: "Unknown",
True: "Yes",
False: "No"
}
return mapping[consensus]

View file

@ -26,7 +26,6 @@ from ietf.ipr.search import iprs_from_docs
from ietf.idrfc.mails import *
from ietf.idrfc.utils import *
from ietf.idrfc.lastcall import request_last_call
from ietf.idrfc.idrfc_wrapper import BallotWrapper
from ietf.doc.utils import *
from ietf.doc.models import *

View file

@ -1,709 +0,0 @@
# 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.
import re, os, datetime
from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.template.loader import render_to_string
from django.template.defaultfilters import truncatewords_html
from django.utils import simplejson as json
from django.utils.decorators import decorator_from_middleware
from django.middleware.gzip import GZipMiddleware
from django.core.urlresolvers import reverse as urlreverse, NoReverseMatch
from django.conf import settings
from ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, DocumentComment
from ietf.idtracker.templatetags.ietf_filters import format_textarea, fill
from ietf.idrfc import markup_txt
from ietf.idrfc.utils import *
from ietf.idrfc.models import RfcIndex, DraftVersions
from ietf.idrfc.idrfc_wrapper import BallotWrapper, IdWrapper, RfcWrapper
from ietf.ietfworkflows.utils import get_full_info_for_draft
from ietf.doc.models import *
from ietf.doc.utils import *
from ietf.utils.history import find_history_active_at
from ietf.ietfauth.decorators import has_role
from ietf.doc.views_status_change import RELATION_SLUGS as status_change_relationships
from ietf.wgcharter.utils import historic_milestones_for_charter
def render_document_top(request, doc, tab, name):
tabs = []
tabs.append(("Document", "document", urlreverse("ietf.idrfc.views_doc.document_main", kwargs=dict(name=name)), True))
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
if doc.type_id in ("draft","conflrev","statchg"):
# if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
tabs.append(("IESG Evaluation Record", "ballot", urlreverse("ietf.idrfc.views_doc.document_ballot", kwargs=dict(name=name)), ballot))
elif doc.type_id == "charter":
tabs.append(("IESG Review", "ballot", urlreverse("ietf.idrfc.views_doc.document_ballot", kwargs=dict(name=name)), ballot))
# FIXME: if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
if doc.type_id not in ("conflrev","statchg"):
tabs.append(("IESG Writeups", "writeup", urlreverse("ietf.idrfc.views_doc.document_writeup", kwargs=dict(name=doc.name)), True))
tabs.append(("History", "history", urlreverse("ietf.idrfc.views_doc.document_history", kwargs=dict(name=doc.name)), True))
name = doc.canonical_name()
if name.startswith("rfc"):
name = "RFC %s" % name[3:]
else:
name += "-" + doc.rev
return render_to_string("idrfc/document_top.html",
dict(doc=doc,
tabs=tabs,
selected=tab,
name=name))
def document_main(request, name, rev=None):
if name.lower().startswith("draft") or name.lower().startswith("rfc"):
if rev != None: # no support for old revisions at the moment
raise Http404()
return document_main_idrfc(request, name, tab="document")
orig_doc = doc = get_object_or_404(Document, docalias__name=name)
group = doc.group
if doc.type_id == 'conflrev':
conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
revisions = []
for h in doc.history_set.order_by("time", "id"):
if h.rev and not h.rev in revisions:
revisions.append(h.rev)
if not doc.rev in revisions:
revisions.append(doc.rev)
snapshot = False
if rev != None:
if rev == doc.rev:
return redirect('doc_view', name=name)
# find the entry in the history
for h in doc.history_set.order_by("-time"):
if rev == h.rev:
snapshot = True
doc = h
break
if not snapshot:
return redirect('doc_view', name=name)
if doc.type_id == "charter":
# find old group, too
gh = find_history_active_at(doc.group, doc.time)
if gh:
group = gh
top = render_document_top(request, doc, "document", name)
telechat = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
if telechat and not telechat.telechat_date:
telechat = None
if telechat and telechat.telechat_date < datetime.date.today():
telechat = None
if doc.type_id == "charter":
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
content = _get_html(filename, os.path.join(settings.CHARTER_PATH, filename), split=False)
ballot_summary = None
if doc.get_state_slug() in ("intrev", "iesgrev"):
active_ballot = doc.active_ballot()
if active_ballot:
ballot_summary = needed_ballot_positions(doc, active_ballot.active_ad_positions().values())
else:
ballot_summary = "No active ballot found."
chartering = get_chartering_type(doc)
# inject milestones from group
milestones = historic_milestones_for_charter(orig_doc, doc.rev)
return render_to_response("idrfc/document_charter.html",
dict(doc=doc,
top=top,
chartering=chartering,
content=content,
txt_url=settings.CHARTER_TXT_URL + filename,
revisions=revisions,
snapshot=snapshot,
telechat=telechat,
ballot_summary=ballot_summary,
group=group,
milestones=milestones,
),
context_instance=RequestContext(request))
if doc.type_id == "conflrev":
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
pathname = os.path.join(settings.CONFLICT_REVIEW_PATH,filename)
if doc.rev == "00" and not os.path.isfile(pathname):
# This could move to a template
content = "A conflict review response has not yet been proposed."
else:
content = _get_html(filename, pathname, split=False)
ballot_summary = None
if doc.get_state_slug() in ("iesgeval"):
ballot_summary = needed_ballot_positions(doc, doc.active_ballot().active_ad_positions().values())
return render_to_response("idrfc/document_conflict_review.html",
dict(doc=doc,
top=top,
content=content,
revisions=revisions,
snapshot=snapshot,
telechat=telechat,
conflictdoc=conflictdoc,
ballot_summary=ballot_summary,
approved_states=('appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent')
),
context_instance=RequestContext(request))
if doc.type_id == "statchg":
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
pathname = os.path.join(settings.STATUS_CHANGE_PATH,filename)
if doc.rev == "00" and not os.path.isfile(pathname):
# This could move to a template
content = "Status change text has not yet been proposed."
else:
content = _get_html(filename, pathname, split=False)
ballot_summary = None
if doc.get_state_slug() in ("iesgeval"):
ballot_summary = needed_ballot_positions(doc, doc.active_ballot().active_ad_positions().values())
if isinstance(doc,Document):
sorted_relations=doc.relateddocument_set.all().order_by('relationship__name')
elif isinstance(doc,DocHistory):
sorted_relations=doc.relateddochistory_set.all().order_by('relationship__name')
else:
sorted_relations=None
return render_to_response("idrfc/document_status_change.html",
dict(doc=doc,
top=top,
content=content,
revisions=revisions,
snapshot=snapshot,
telechat=telechat,
ballot_summary=ballot_summary,
approved_states=('appr-pend','appr-sent'),
sorted_relations=sorted_relations,
),
context_instance=RequestContext(request))
raise Http404()
def document_history(request, name):
# todo: remove need for specific handling of drafts by porting the
# two event text hacks
if name.lower().startswith("draft") or name.lower().startswith("rfc"):
return document_main_idrfc(request, name, "history")
doc = get_object_or_404(Document, docalias__name=name)
top = render_document_top(request, doc, "history", name)
# pick up revisions from events
diff_revisions = []
diffable = name.startswith("draft") or name.startswith("charter") or name.startswith("conflict-review") or name.startswith("status-change")
if diffable:
diff_documents = [ doc ]
diff_documents.extend(Document.objects.filter(docalias__relateddocument__source=doc, docalias__relateddocument__relationship="replaces"))
seen = set()
for e in NewRevisionDocEvent.objects.filter(type="new_revision", doc__in=diff_documents).select_related('doc').order_by("-time", "-id"):
if (e.doc.name, e.rev) in seen:
continue
seen.add((e.doc.name, e.rev))
url = ""
if name.startswith("charter"):
h = find_history_active_at(e.doc, e.time)
url = settings.CHARTER_TXT_URL + ("%s-%s.txt" % ((h or doc).canonical_name(), e.rev))
elif name.startswith("conflict-review"):
h = find_history_active_at(e.doc, e.time)
url = settings.CONFLICT_REVIEW_TXT_URL + ("%s-%s.txt" % ((h or doc).canonical_name(), e.rev))
elif name.startswith("status-change"):
h = find_history_active_at(e.doc, e.time)
url = settings.STATUS_CHANGE_TXT_URL + ("%s-%s.txt" % ((h or doc).canonical_name(), e.rev))
elif name.startswith("draft"):
# rfcdiff tool has special support for IDs
url = e.doc.name + "-" + e.rev
diff_revisions.append((e.doc.name, e.rev, e.time, url))
# grab event history
events = doc.docevent_set.all().order_by("-time", "-id").select_related("by")
augment_events_with_revision(doc, events)
return render_to_response("idrfc/document_history.html",
dict(doc=doc,
top=top,
diff_revisions=diff_revisions,
events=events,
),
context_instance=RequestContext(request))
def document_writeup(request, name):
if name.lower().startswith("draft") or name.lower().startswith("rfc"):
# todo: migrate idrfc to pattern below
return document_main_idrfc(request, name, "writeup")
doc = get_object_or_404(Document, docalias__name=name)
top = render_document_top(request, doc, "writeup", name)
writeups = []
if doc.type_id == "charter":
e = doc.latest_event(WriteupDocEvent, type="changed_review_announcement")
writeups.append(("WG Review Announcement",
e.text if e else "",
urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="review"))))
e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
writeups.append(("WG Action Announcement",
e.text if e else "",
urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="action"))))
if doc.latest_event(BallotDocEvent, type="created_ballot"):
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
writeups.append(("Ballot Announcement",
e.text if e else "",
urlreverse("ietf.wgcharter.views.ballot_writeupnotes", kwargs=dict(name=doc.name))))
if not writeups:
raise Http404()
return render_to_response("idrfc/document_writeup.html",
dict(doc=doc,
top=top,
writeups=writeups,
can_edit=has_role(request.user, ("Area Director", "Secretariat")),
),
context_instance=RequestContext(request))
def document_shepherd_writeup(request, name):
doc = get_object_or_404(Document, docalias__name=name)
lastwriteup = doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup")
if lastwriteup:
writeup_text = lastwriteup.text
else:
writeup_text = "(There is no shepherd's writeup available for this document)"
return render_to_response("idrfc/shepherd_writeup.html",
dict(doc=doc,
writeup=writeup_text,
can_edit=can_edit_shepherd_writeup(doc,request.user)
),
context_instance=RequestContext(request))
def document_ballot_content(request, doc, ballot_id, editable=True):
"""Render HTML string with content of ballot page."""
all_ballots = list(BallotDocEvent.objects.filter(doc=doc, type="created_ballot").order_by("time"))
augment_events_with_revision(doc, all_ballots)
ballot = None
if ballot_id != None:
ballot_id = int(ballot_id)
for b in all_ballots:
if b.id == ballot_id:
ballot = b
break
elif all_ballots:
ballot = all_ballots[-1]
if not ballot:
raise Http404
deferred = doc.active_defer_event()
positions = ballot.all_positions()
# put into position groups
position_groups = []
for n in BallotPositionName.objects.filter(slug__in=[p.pos_id for p in positions]).order_by('order'):
g = (n, [p for p in positions if p.pos_id == n.slug])
g[1].sort(key=lambda p: (p.old_ad, p.ad.plain_name()))
if n.blocking:
position_groups.insert(0, g)
else:
position_groups.append(g)
summary = needed_ballot_positions(doc, [p for p in positions if not p.old_ad])
text_positions = [p for p in positions if p.discuss or p.comment]
text_positions.sort(key=lambda p: (p.old_ad, p.ad.plain_name()))
ballot_open = not BallotDocEvent.objects.filter(doc=doc,
type__in=("closed_ballot", "created_ballot"),
time__gt=ballot.time,
ballot_type=ballot.ballot_type)
if not ballot_open:
editable = False
return render_to_string("idrfc/document_ballot_content.html",
dict(doc=doc,
ballot=ballot,
position_groups=position_groups,
text_positions=text_positions,
editable=editable,
ballot_open=ballot_open,
deferred=deferred,
summary=summary,
all_ballots=all_ballots,
),
context_instance=RequestContext(request))
def document_ballot(request, name, ballot_id=None):
doc = get_object_or_404(Document, docalias__name=name)
top = render_document_top(request, doc, "ballot", name)
c = document_ballot_content(request, doc, ballot_id, editable=True)
return render_to_response("idrfc/document_ballot.html",
dict(doc=doc,
top=top,
ballot_content=c,
),
context_instance=RequestContext(request))
def document_json(request, name):
doc = get_object_or_404(Document, docalias__name=name)
def extract_name(s):
return s.name if s else None
data = {}
data["name"] = doc.name
data["rev"] = doc.rev
data["time"] = doc.time.strftime("%Y-%m-%d %H:%M:%S")
data["group"] = None
if doc.group:
data["group"] = dict(
name=doc.group.name,
type=extract_name(doc.group.type),
acronym=doc.group.acronym)
data["expires"] = doc.expires.strftime("%Y-%m-%d %H:%M:%S") if doc.expires else None
data["title"] = doc.title
data["abstract"] = doc.abstract
data["aliases"] = list(doc.docalias_set.values_list("name", flat=True))
data["state"] = extract_name(doc.get_state())
data["intended_std_level"] = extract_name(doc.intended_std_level)
data["std_level"] = extract_name(doc.std_level)
data["authors"] = [
dict(name=e.person.name,
email=e.address,
affiliation=e.person.affiliation)
for e in Email.objects.filter(documentauthor__document=doc).select_related("person").order_by("documentauthor__order")
]
data["shepherd"] = doc.shepherd.formatted_email() if doc.shepherd else None
data["ad"] = doc.ad.role_email("ad").formatted_email() if doc.ad else None
if doc.type_id == "draft":
data["iesg_state"] = extract_name(doc.get_state("draft-iesg"))
data["rfceditor_state"] = extract_name(doc.get_state("draft-rfceditor"))
data["iana_review_state"] = extract_name(doc.get_state("draft-iana-review"))
data["iana_action_state"] = extract_name(doc.get_state("draft-iana-action"))
if doc.stream_id in ("ietf", "irtf", "iab"):
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
data["consensus"] = e.consensus if e else None
data["stream"] = extract_name(doc.stream)
return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain')
def _get_html(key, filename, split=True):
return get_document_content(key, filename, split=split, markup=True)
def include_text(request):
include_text = request.GET.get( 'include_text' )
if "full_draft" in request.COOKIES:
if request.COOKIES["full_draft"] == "on":
include_text = 1
return include_text
def document_main_rfc(request, rfc_number, tab):
rfci = get_object_or_404(RfcIndex, rfc_number=rfc_number, states__type="draft", states__slug="rfc")
rfci.viewing_as_rfc = True
doc = RfcWrapper(rfci)
info = {}
info['is_rfc'] = True
info['has_pdf'] = (".pdf" in doc.file_types())
info['has_txt'] = (".txt" in doc.file_types())
info['has_ps'] = (".ps" in doc.file_types())
if info['has_txt']:
(content1, content2) = _get_html(
"rfc"+str(rfc_number)+",html",
os.path.join(settings.RFC_PATH, "rfc"+str(rfc_number)+".txt"))
else:
content1 = ""
content2 = ""
underlying_document = doc.underlying_document()
if underlying_document:
info['status_changes'] = ', '.join([ rel.source.canonical_name() for rel in RelatedDocument.objects.filter(relationship__in=status_change_relationships,target__document=underlying_document) if rel.source.get_state_slug() in ('appr-sent','appr-pend')])
info['proposed_status_changes'] = ', '.join([ rel.source.canonical_name() for rel in RelatedDocument.objects.filter(relationship__in=status_change_relationships,target__document=underlying_document) if rel.source.get_state_slug() in ('needshep','adrev','iesgeval','defer','appr-pr')])
history = _get_history(doc, None)
template = "idrfc/doc_tab_%s" % tab
if tab == "document":
template += "_rfc"
return render_to_response(template + ".html",
{'content1':content1, 'content2':content2,
'doc':doc, 'info':info, 'tab':tab,
'include_text':include_text(request),
'history':history},
context_instance=RequestContext(request));
@decorator_from_middleware(GZipMiddleware)
def document_main_idrfc(request, name, tab):
r = re.compile("^rfc([1-9][0-9]*)$")
m = r.match(name)
if m:
return document_main_rfc(request, int(m.group(1)), tab)
id = get_object_or_404(InternetDraft, filename=name)
doc = IdWrapper(id)
info = {}
info['has_pdf'] = (".pdf" in doc.file_types())
info['is_rfc'] = False
info['conflict_reviews'] = [ rel.source for alias in id.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='conflrev') ]
info['rfc_editor_state'] = id.get_state("draft-rfceditor")
info['iana_review_state'] = id.get_state("draft-iana-review")
info['iana_action_state'] = id.get_state("draft-iana-action")
info["consensus"] = None
if id.stream_id in ("ietf", "irtf", "iab"):
e = id.latest_event(ConsensusDocEvent, type="changed_consensus")
info["consensus"] = nice_consensus(e and e.consensus)
info["can_edit_consensus"] = can_edit_consensus(id, request.user)
info["can_edit_intended_std_level"] = can_edit_intended_std_level(id, request.user)
info["can_edit_shepherd"] = can_edit_shepherd(id, request.user)
(content1, content2) = _get_html(
str(name)+","+str(id.revision)+",html",
os.path.join(settings.INTERNET_DRAFT_PATH, name+"-"+id.revision+".txt"))
versions = _get_versions(id)
history = _get_history(doc, versions)
template = "idrfc/doc_tab_%s" % tab
if tab == "document":
template += "_id"
return render_to_response(template + ".html",
{'content1':content1, 'content2':content2,
'doc':doc, 'info':info, 'tab':tab,
'include_text':include_text(request),
'stream_info': get_full_info_for_draft(id),
'milestones': id.groupmilestone_set.filter(state="active"),
'versions':versions, 'history':history},
context_instance=RequestContext(request));
# doc is either IdWrapper or RfcWrapper
def _get_history(doc, versions):
results = []
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
versions = [] # clear versions
event_holder = doc._draft if hasattr(doc, "_draft") else doc._rfcindex
for e in event_holder.docevent_set.all().select_related('by').order_by('-time', 'id'):
info = {}
if e.type == "new_revision":
filename = u"%s-%s" % (e.doc.name, e.newrevisiondocevent.rev)
e.desc = 'New version available: <a href="http://tools.ietf.org/id/%s.txt">%s</a>' % (filename, filename)
if int(e.newrevisiondocevent.rev) != 0:
e.desc += ' (<a href="http:%s?url2=%s">diff from -%02d</a>)' % (settings.RFCDIFF_PREFIX, filename, int(e.newrevisiondocevent.rev) - 1)
info["dontmolest"] = True
multiset_ballot_text = "This was part of a ballot set with: "
if e.desc.startswith(multiset_ballot_text):
names = [ n.strip() for n in e.desc[len(multiset_ballot_text):].split(",") ]
e.desc = multiset_ballot_text + ", ".join(u'<a href="%s">%s</a>' % (urlreverse("doc_view", kwargs={'name': n }), n) for n in names)
info["dontmolest"] = True
info['text'] = e.desc
info['by'] = e.by.plain_name()
info['textSnippet'] = truncatewords_html(info['text'], 25).replace('<br>',' ')
info['snipped'] = info['textSnippet'][-3:] == "..." and e.type != "new_revision"
results.append({'comment':e, 'info':info, 'date':e.time, 'is_com':True})
prev_rev = "00"
# actually, we're already sorted and this ruins the sort from
# the ids which is sometimes needed, so the function should be
# rewritten to not rely on a resort
results.sort(key=lambda x: x['date'])
for o in results:
e = o["comment"]
if e.type == "new_revision":
e.version = e.newrevisiondocevent.rev
else:
e.version = prev_rev
prev_rev = e.version
else:
if doc.is_id_wrapper:
comments = DocumentComment.objects.filter(document=doc.tracker_id).exclude(rfc_flag=1)
else:
comments = DocumentComment.objects.filter(document=doc.rfc_number,rfc_flag=1)
if len(comments) > 0:
# also include rfc_flag=NULL, but only if at least one
# comment with rfc_flag=1 exists (usually NULL means same as 0)
comments = DocumentComment.objects.filter(document=doc.rfc_number).exclude(rfc_flag=0)
for comment in comments.order_by('-date','-time','-id').filter(public_flag=1).select_related('created_by'):
info = {}
info['text'] = comment.comment_text
info['by'] = comment.get_fullname()
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
info['snipped'] = info['textSnippet'][-3:] == "..."
results.append({'comment':comment, 'info':info, 'date':comment.datetime(), 'is_com':True})
if doc.is_id_wrapper and versions:
for v in versions:
if v['draft_name'] == doc.draft_name:
v = dict(v) # copy it, since we're modifying datetimes later
v['is_rev'] = True
results.insert(0, v)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES and doc.is_id_wrapper and doc.draft_status == "Expired" and doc._draft.expiration_date:
results.append({'is_text':True, 'date':doc._draft.expiration_date, 'text':'Draft expired'})
if not settings.USE_DB_REDESIGN_PROXY_CLASSES and doc.is_rfc_wrapper:
text = 'RFC Published'
if doc.draft_name:
try:
text = 'RFC Published (see <a href="%s">%s</a> for earlier history)' % (urlreverse('doc_view', args=[doc.draft_name]),doc.draft_name)
except NoReverseMatch:
pass
results.append({'is_text':True, 'date':doc.publication_date, 'text':text})
# convert plain dates to datetimes (required for sorting)
for x in results:
if not isinstance(x['date'], datetime.datetime):
if x['date']:
x['date'] = datetime.datetime.combine(x['date'], datetime.time(0,0,0))
else:
x['date'] = datetime.datetime(1970,1,1)
results.sort(key=lambda x: x['date'])
results.reverse()
return results
# takes InternetDraft instance
def _get_versions(draft, include_replaced=True):
ov = []
ov.append({"draft_name":draft.filename, "revision":draft.revision_display(), "date":draft.revision_date})
if include_replaced:
draft_list = [draft]+list(draft.replaces_set.all())
else:
draft_list = [draft]
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.doc.models import NewRevisionDocEvent
for e in NewRevisionDocEvent.objects.filter(type="new_revision", doc__in=draft_list).select_related('doc').order_by("-time", "-id"):
if not (e.doc.name == draft.name and e.rev == draft.rev):
ov.append(dict(draft_name=e.doc.name, revision=e.rev, date=e.time.date()))
return ov
for d in draft_list:
for v in DraftVersions.objects.filter(filename=d.filename).order_by('-revision'):
if (d.filename == draft.filename) and (draft.revision_display() == v.revision):
continue
ov.append({"draft_name":d.filename, "revision":v.revision, "date":v.revision_date})
return ov
def get_ballot(name):
from ietf.doc.models import DocAlias
alias = get_object_or_404(DocAlias, name=name)
d = alias.document
id = None
bw = None
dw = None
if (d.type_id=='draft'):
id = get_object_or_404(InternetDraft, name=d.name)
try:
if not id.ballot.ballot_issued:
raise Http404
except BallotInfo.DoesNotExist:
raise Http404
bw = BallotWrapper(id) # XXX Fixme: Eliminate this as we go forward
# Python caches ~100 regex'es -- explicitly compiling it inside a method
# (where you then throw away the compiled version!) doesn't make sense at
# all.
if re.search("^rfc([1-9][0-9]*)$", name):
id.viewing_as_rfc = True
dw = RfcWrapper(id)
else:
dw = IdWrapper(id)
# XXX Fixme: Eliminate 'dw' as we go forward
try:
b = d.latest_event(BallotDocEvent, type="created_ballot")
except BallotDocEvent.DoesNotExist:
raise Http404
return (bw, dw, b, d)
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_html(request, name):
bw, dw, ballot, doc = get_ballot(name)
content = document_ballot_content(request, doc, ballot.pk, editable=True)
return HttpResponse(content)
def ballot_tsv(request, name):
ballot, doc, b, d = get_ballot(name)
return HttpResponse(render_to_string('idrfc/ballot.tsv', {'ballot':ballot}, RequestContext(request)), content_type="text/plain")
def ballot_json(request, name):
ballot, doc, b, d = get_ballot(name)
response = HttpResponse(mimetype='text/plain')
response.write(json.dumps(ballot.dict(), indent=2))
return response

View file

@ -13,11 +13,12 @@ from django.utils.html import strip_tags
from django.db.models import Max
from django.conf import settings
from django.forms.util import ErrorList
from django.contrib.auth.decorators import login_required
from ietf.utils.mail import send_mail_text, send_mail_message
from ietf.ietfauth.decorators import group_required
from ietf.ietfauth.decorators import group_required, has_role, role_required
from ietf.ietfauth.utils import user_is_person
from ietf.idtracker.templatetags.ietf_filters import in_group
from ietf.ietfauth.decorators import has_role, role_required
from ietf.idtracker.models import *
from ietf.iesg.models import *
from ietf.idrfc.mails import *
@ -225,14 +226,21 @@ class ChangeStreamForm(forms.Form):
stream = forms.ModelChoiceField(StreamName.objects.exclude(slug="legacy"), required=False)
comment = forms.CharField(widget=forms.Textarea, required=False)
@group_required('Area_Director','Secretariat')
@login_required
def change_stream(request, name):
"""Change the stream of a Document of type 'draft' , notifying parties as necessary
"""Change the stream of a Document of type 'draft', notifying parties as necessary
and logging the change as a comment."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.type_id=='draft':
raise Http404()
if not (has_role(request.user, ("Area Director", "Secretariat")) or
(request.user.is_authenticated() and
Role.objects.filter(name="chair",
group__acronym__in=StreamName.objects.values_list("slug", flat=True),
person__user=request.user))):
return HttpResponseForbidden("You do not have permission to view this page")
login = request.user.get_profile()
if request.method == 'POST':
@ -248,7 +256,7 @@ def change_stream(request, name):
doc.stream = new_stream
e = DocEvent(doc=doc,by=login,type='changed_document')
e.desc = u"Stream changed to <b>%s</b> from %s"% (new_stream,old_stream)
e.desc = u"Stream changed to <b>%s</b> from %s"% (new_stream, old_stream or "None")
e.save()
email_desc = e.desc
@ -287,7 +295,8 @@ def change_intention(request, name):
if doc.type_id != 'draft':
raise Http404
if not can_edit_intended_std_level(doc, request.user):
if not (has_role(request.user, ("Secretariat", "Area Director"))
or is_authorized_in_doc_stream(request.user, doc)):
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
login = request.user.get_profile()
@ -396,8 +405,7 @@ class EditInfoFormREDESIGN(forms.Form):
self.standard_fields = [x for x in self.visible_fields() if x.name not in ('returning_item',)]
def clean_note(self):
# note is stored munged in the database
return self.cleaned_data['note'].replace('\n', '<br>').replace('\r', '').replace(' ', '&nbsp; ')
return self.cleaned_data['note'].replace('\r', '').strip()
def get_initial_notify(doc):
@ -532,7 +540,7 @@ def edit_infoREDESIGN(request, name):
area=doc.group_id,
ad=doc.ad_id,
notify=doc.notify,
note=dehtmlify_textarea_text(doc.note),
note=doc.note,
telechat_date=initial_telechat_date,
returning_item=initial_returning_item,
)
@ -827,15 +835,16 @@ class IESGNoteForm(forms.Form):
note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False)
def clean_note(self):
# note is stored munged in the database
return self.cleaned_data['note'].replace('\n', '<br>').replace('\r', '').replace(' ', '&nbsp; ')
# not muning the database content to use html line breaks --
# that has caused a lot of pain in the past.
return self.cleaned_data['note'].replace('\r', '').strip()
@group_required("Area Director", "Secretariat")
def edit_iesg_note(request, name):
doc = get_object_or_404(Document, type="draft", name=name)
login = request.user.get_profile()
initial = dict(note=dehtmlify_textarea_text(doc.note))
initial = dict(note=doc.note)
if request.method == "POST":
form = IESGNoteForm(request.POST, initial=initial)
@ -883,7 +892,10 @@ def edit_shepherd_writeup(request, name):
"""Change this document's shepherd writeup"""
doc = get_object_or_404(Document, type="draft", name=name)
if not can_edit_shepherd_writeup(doc, request.user):
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
can_edit_shepherd_writeup = can_edit_stream_info or user_is_person(request.user, doc.shepherd) or has_role(request.user, ["Area Director"])
if not can_edit_shepherd_writeup:
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
login = request.user.get_profile()
@ -950,7 +962,9 @@ def edit_shepherd(request, name):
# TODO - this shouldn't be type="draft" specific
doc = get_object_or_404(Document, type="draft", name=name)
if not can_edit_shepherd(doc, request.user):
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
if not can_edit_stream_info:
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
if request.method == 'POST':
@ -1036,7 +1050,8 @@ def edit_consensus(request, name):
doc = get_object_or_404(Document, type="draft", name=name)
if not can_edit_consensus(doc, request.user):
if not (has_role(request.user, ("Secretariat", "Area Director"))
or is_authorized_in_doc_stream(request.user, doc)):
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")

View file

@ -44,6 +44,10 @@ from ietf.idrfc.idrfc_wrapper import IdWrapper,RfcWrapper,IdRfcWrapper
from ietf.utils import normalize_draftname
from django.conf import settings
from ietf.doc.models import *
from ietf.person.models import *
from ietf.group.models import *
class SearchForm(forms.Form):
name = forms.CharField(required=False)
rfcs = forms.BooleanField(required=False,initial=True)
@ -54,15 +58,27 @@ class SearchForm(forms.Form):
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)
group = forms.CharField(required=False)
area = forms.ModelChoiceField(Area.active_areas(), empty_label="any area", required=False)
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(IDState.objects.all(), empty_label="any state", required=False)
state = forms.ModelChoiceField(State.objects.filter(type="draft-iesg"), empty_label="any state", required=False)
subState = forms.ChoiceField(choices=(), required=False)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
self.fields['ad'].choices = [('', 'any AD')] + [(ad.id, "%s %s" % (ad.first_name, ad.last_name)) for ad in IESGLogin.objects.filter(user_level=1).order_by('last_name')] + [('-99', '------------------')] + [(ad.id, "%s %s" % (ad.first_name, ad.last_name)) for ad in IESGLogin.objects.filter(user_level=2).order_by('last_name')]
self.fields['subState'].choices = [('', 'any substate'), ('0', 'no substate')] + [(state.sub_state_id, state.sub_state) for state in IDSubState.objects.all()]
responsible = Document.objects.values_list('ad', flat=True).distinct()
active_ads = list(Person.objects.filter(role__name="ad",
role__group__type="area",
role__group__state="active").distinct())
inactive_ads = list(((Person.objects.filter(pk__in=responsible) | Person.objects.filter(role__name="pre-ad",
role__group__type="area",
role__group__state="active")).distinct())
.exclude(pk__in=[x.pk for x in active_ads]))
extract_last_name = lambda x: x.name_parts()[3]
active_ads.sort(key=extract_last_name)
inactive_ads.sort(key=extract_last_name)
self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.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'))]
def clean_name(self):
value = self.cleaned_data.get('name','')
return normalize_draftname(value)
@ -73,9 +89,9 @@ class SearchForm(forms.Form):
q['by'] = None
else:
for k in ('author','group','area','ad'):
if (q['by'] == k) and not q[k]:
if (q['by'] == k) and (k not in q or not q[k]):
q['by'] = None
if (q['by'] == 'state') and not (q['state'] or q['subState']):
if (q['by'] == 'state') and (not 'state' in q or not 'subState' in q or not (q['state'] or q['subState'])):
q['by'] = None
# Reset other fields
for k in ('author','group','area','ad'):
@ -88,7 +104,7 @@ class SearchForm(forms.Form):
q['state'] = ""
q['subState'] = ""
return q
def search_query(query_original, sort_by=None):
query = dict(query_original.items())
drafts = query['activeDrafts'] or query['oldDrafts']
@ -97,6 +113,7 @@ def search_query(query_original, sort_by=None):
# 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, ''))
@ -107,388 +124,156 @@ def search_query(query_original, sort_by=None):
idresults = []
rfcresults = []
MAX = 500
maxReached = False
prefix = ""
q_objs = []
if query['by'] in ('ad','state'):
prefix = "draft__"
if query['name']:
q_objs.append(Q(**{prefix+"filename__icontains":query['name']})|Q(**{prefix+"title__icontains":query['name']}))
docs = InternetDraft.objects.all()
if query['by'] == 'author':
q_objs.append(Q(**{prefix+"authors__person__last_name__icontains":query['author']}))
elif query['by'] == 'group':
q_objs.append(Q(**{prefix+"group__acronym":query['group']}))
elif query['by'] == 'area':
q_objs.append(Q(**{prefix+"group__ietfwg__areagroup__area":query['area']}))
elif query['by'] == 'ad':
q_objs.append(Q(job_owner=query['ad']))
elif query['by'] == 'state':
if query['state']:
q_objs.append(Q(cur_state=query['state']))
if query['subState']:
q_objs.append(Q(cur_sub_state=query['subState']))
if (not query['rfcs']) and query['activeDrafts'] and (not query['oldDrafts']):
q_objs.append(Q(**{prefix+"status":1}))
elif query['rfcs'] and query['activeDrafts'] and (not query['oldDrafts']):
q_objs.append(Q(**{prefix+"status":1})|Q(**{prefix+"status":3}))
elif query['rfcs'] and (not drafts):
q_objs.append(Q(**{prefix+"status":3}))
if prefix:
q_objs.append(Q(rfc_flag=0))
matches = IDInternal.objects.filter(*q_objs)
else:
matches = InternetDraft.objects.filter(*q_objs)
if not query['activeDrafts']:
matches = matches.exclude(Q(**{prefix+"status":1}))
if not query['rfcs']:
matches = matches.exclude(Q(**{prefix+"status":3}))
if prefix:
matches = [id.draft for id in matches[:MAX]]
else:
matches = matches[:MAX]
if len(matches) == MAX:
maxReached = True
for id in matches:
if id.status.status == 'RFC':
rfcresults.append([id.rfc_number, id, None, None])
# name
if query["name"]:
docs = docs.filter(Q(docalias__name__icontains=query["name"]) |
Q(title__icontains=query["name"])).distinct()
# rfc/active/old check buttons
allowed_states = []
if query["rfcs"]:
allowed_states.append("rfc")
if query["activeDrafts"]:
allowed_states.append("active")
if query["oldDrafts"]:
allowed_states.extend(['repl', 'expired', 'auth-rm', 'ietf-rm'])
docs = docs.filter(states__type="draft", states__slug__in=allowed_states)
# 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"])
elif by == "area":
docs = docs.filter(Q(group__type="wg", group__parent=query["area"]) |
Q(group=query["area"])).distinct()
elif by == "ad":
docs = docs.filter(ad=query["ad"])
elif by == "state":
if query["state"]:
docs = docs.filter(states=query["state"])
if query["subState"]:
docs = docs.filter(tags=query["subState"])
# evaluate and fill in values with aggregate queries to avoid
# too many individual 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:
idresults.append([id])
r.canonical_name = (lambda x: lambda: x)(r.name)
# Next, search RFCs
if query['rfcs']:
q_objs = []
searchRfcIndex = True
if query['name']:
r = re.compile("^\s*(?:RFC)?\s*(\d+)\s*$", re.IGNORECASE)
m = r.match(query['name'])
if m:
q_objs.append(Q(rfc_number__contains=m.group(1))|Q(title__icontains=query['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:]))
# sort
def sort_key(d):
res = []
canonical = d.canonical_name()
if canonical.startswith('rfc'):
rfc_num = int(canonical[3:])
else:
rfc_num = None
if rfc_num != None:
res.append(2)
elif d.get_state_slug() == "active":
res.append(1)
else:
res.append(3)
if sort_by == "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":
if rfc_num != None:
res.append(rfc_num)
else:
q_objs.append(Q(title__icontains=query['name']))
if query['by'] == 'author':
q_objs.append(Q(authors__icontains=query['author']))
elif query['by'] == 'group':
# We prefer searching RfcIndex, but it doesn't have group info
searchRfcIndex = False
q_objs.append(Q(group_acronym=query['group']))
elif query['by'] == 'area':
# Ditto for area
searchRfcIndex = False
q_objs.append(Q(area_acronym=query['area']))
elif query['by'] == 'ad':
numbers = IDInternal.objects.filter(rfc_flag=1,job_owner=query['ad']).values_list('draft_id',flat=True)
q_objs.append(Q(rfc_number__in=numbers))
elif query['by'] == 'state':
numbers_q = [Q(rfc_flag=1)]
if query['state']:
numbers_q.append(Q(cur_state=query['state']))
if query['subState']:
numbers_q.append(Q(cur_state=query['subState']))
numbers = IDInternal.objects.filter(*numbers_q).values_list('draft_id',flat=True)
q_objs.append(Q(rfc_number__in=numbers))
if searchRfcIndex:
matches = RfcIndex.objects.filter(*q_objs)[:MAX]
else:
matches = Rfc.objects.filter(*q_objs)[:MAX]
if len(matches) == MAX:
maxReached = True
for rfc in matches:
found = False
for r2 in rfcresults:
if r2[0] == rfc.rfc_number:
if searchRfcIndex:
r2[3] = rfc
else:
r2[2] = rfc
found = True
if not found:
if searchRfcIndex:
rfcresults.append([rfc.rfc_number, None, None, rfc])
res.append(d.get_state().order)
elif sort_by == "ipr":
res.append(d.name)
elif sort_by == "ad":
if rfc_num != None:
res.append(rfc_num)
elif d.get_state_slug() == "active":
if d.get_state("draft-iesg"):
res.append(d.get_state("draft-iesg").order)
else:
rfcresults.append([rfc.rfc_number, None, rfc, None])
# Find missing InternetDraft objects
for r in rfcresults:
if not r[1]:
ids = InternetDraft.objects.filter(rfc_number=r[0])
if len(ids) >= 1:
r[1] = ids[0]
if not r[1] and r[3] and r[3].draft:
ids = InternetDraft.objects.filter(filename=r[3].draft)
if len(ids) >= 1:
r[1] = ids[0]
# Finally, find missing RFC objects
for r in rfcresults:
if not r[2]:
rfcs = Rfc.objects.filter(rfc_number=r[0])
if len(rfcs) >= 1:
r[2] = rfcs[0]
if not r[3]:
rfcs = RfcIndex.objects.filter(rfc_number=r[0])
if len(rfcs) >= 1:
r[3] = rfcs[0]
# TODO: require that RfcIndex is present?
results = []
for res in idresults+rfcresults:
if len(res)==1:
doc = IdRfcWrapper(IdWrapper(res[0]), None)
results.append(doc)
res.append(0)
else:
d = None
r = None
if res[1]:
d = IdWrapper(res[1])
if res[3]:
r = RfcWrapper(res[3])
if d or r:
doc = IdRfcWrapper(d, r)
results.append(doc)
results.sort(key=lambda obj: obj.view_sort_key(sort_by))
if rfc_num != None:
res.append(rfc_num)
else:
res.append(canonical)
return res
results.sort(key=sort_key)
meta = {}
if maxReached:
if len(docs) == MAX:
meta['max'] = MAX
if query['by']:
meta['advanced'] = True
return (results,meta)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.doc.models import *
from ietf.person.models import *
from ietf.group.models import *
# finally wrap in old wrappers
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)
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))
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)
group = forms.CharField(required=False)
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(used=True, type="draft-iesg"), empty_label="any state", required=False)
subState = forms.ChoiceField(choices=(), required=False)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
responsible = Document.objects.values_list('ad', flat=True).distinct()
active_ads = list(Person.objects.filter(role__name="ad",
role__group__type="area",
role__group__state="active").distinct())
inactive_ads = list(((Person.objects.filter(pk__in=responsible) | Person.objects.filter(role__name="pre-ad",
role__group__type="area",
role__group__state="active")).distinct())
.exclude(pk__in=[x.pk for x in active_ads]))
extract_last_name = lambda x: x.name_parts()[3]
active_ads.sort(key=extract_last_name)
inactive_ads.sort(key=extract_last_name)
self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.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'))]
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]):
q['by'] = None
if (q['by'] == 'state') and (not 'state' in q or not 'subState' in q or not (q['state'] or q['subState'])):
q['by'] = None
# Reset other fields
for k in ('author','group','area','ad'):
if q['by'] != k:
self.data[k] = ""
q[k] = ""
if q['by'] != 'state':
self.data['state'] = ""
self.data['subState'] = ""
q['state'] = ""
q['subState'] = ""
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']):
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()
# name
if query["name"]:
docs = docs.filter(Q(docalias__name__icontains=query["name"]) |
Q(title__icontains=query["name"])).distinct()
# rfc/active/old check buttons
allowed_states = []
if query["rfcs"]:
allowed_states.append("rfc")
if query["activeDrafts"]:
allowed_states.append("active")
if query["oldDrafts"]:
allowed_states.extend(['repl', 'expired', 'auth-rm', 'ietf-rm'])
docs = docs.filter(states__type="draft", states__slug__in=allowed_states)
# 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"])
elif by == "area":
docs = docs.filter(Q(group__type="wg", group__parent=query["area"]) |
Q(group=query["area"])).distinct()
elif by == "ad":
docs = docs.filter(ad=query["ad"])
elif by == "state":
if query["state"]:
docs = docs.filter(states=query["state"])
if query["subState"]:
docs = docs.filter(tags=query["subState"])
# evaluate and fill in values with aggregate queries to avoid
# too many individual 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:]))
# sort
def sort_key(d):
res = []
canonical = d.canonical_name()
if canonical.startswith('rfc'):
rfc_num = int(canonical[3:])
else:
rfc_num = None
if rfc_num != None:
res.append(2)
elif d.get_state_slug() == "active":
res.append(1)
else:
res.append(3)
if sort_by == "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":
if rfc_num != None:
res.append(rfc_num)
else:
res.append(d.get_state().order)
elif sort_by == "ipr":
res.append(d.name)
elif sort_by == "ad":
if rfc_num != None:
res.append(rfc_num)
elif d.get_state_slug() == "active":
if d.get_state("draft-iesg"):
res.append(d.get_state("draft-iesg").order)
else:
res.append(0)
else:
if rfc_num != None:
res.append(rfc_num)
else:
res.append(canonical)
return res
results.sort(key=sort_key)
meta = {}
if len(docs) == MAX:
meta['max'] = MAX
if query['by']:
meta['advanced'] = True
# finally wrap in old wrappers
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))
return (wrapped_results, meta)
return (wrapped_results, meta)
def generate_query_string(request, ignore_list):

View file

@ -4,7 +4,8 @@ import textwrap
from django import template
from django.conf import settings
from django.utils.html import escape, fix_ampersands
from django.template.defaultfilters import linebreaksbr, wordwrap, stringfilter, urlize, truncatewords_html
from django.utils.text import truncate_html_words
from django.template.defaultfilters import linebreaksbr, wordwrap, stringfilter, urlize
from django.template import resolve_variable
from django.utils.safestring import mark_safe, SafeData
from django.utils import simplejson
@ -221,9 +222,9 @@ def urlize_ietf_docs(string, autoescape=None):
if autoescape and not isinstance(string, SafeData):
string = escape(string)
string = re.sub("(?<!>)(RFC ?)0{0,3}(\d+)", "<a href=\"/doc/rfc\\2/\">\\1\\2</a>", string)
string = re.sub("(?<!>)(BCP ?)0{0,3}(\d+)", "<a href=\"http://tools.ietf.org/html/bcp\\2/\">\\1\\2</a>", string)
string = re.sub("(?<!>)(STD ?)0{0,3}(\d+)", "<a href=\"http://tools.ietf.org/html/std\\2/\">\\1\\2</a>", string)
string = re.sub("(?<!>)(FYI ?)0{0,3}(\d+)", "<a href=\"http://tools.ietf.org/html/fyi\\2/\">\\1\\2</a>", string)
string = re.sub("(?<!>)(BCP ?)0{0,3}(\d+)", "<a href=\"/doc/bcp\\2/\">\\1\\2</a>", string)
string = re.sub("(?<!>)(STD ?)0{0,3}(\d+)", "<a href=\"/doc/std\\2/\">\\1\\2</a>", string)
string = re.sub("(?<!>)(FYI ?)0{0,3}(\d+)", "<a href=\"/doc/fyi\\2/\">\\1\\2</a>", string)
string = re.sub("(?<!>)(draft-[-0-9a-zA-Z._+]+)", "<a href=\"/doc/\\1/\">\\1</a>", string)
string = re.sub("(?<!>)(conflict-review-[-0-9a-zA-Z._+]+)", "<a href=\"/doc/\\1/\">\\1</a>", string)
string = re.sub("(?<!>)(status-change-[-0-9a-zA-Z._+]+)", "<a href=\"/doc/\\1/\">\\1</a>", string)
@ -461,9 +462,14 @@ def ad_area(user):
@register.filter
def format_history_text(text):
"""Run history text through some cleaning and add ellipsis if it's too long."""
full = mark_safe(sanitize_html(keep_spacing(linebreaksbr(urlize(mark_safe(text))))))
snippet = truncatewords_html(format_textarea(text), 25)
if snippet[-3:] == "...":
full = mark_safe(text)
if text.startswith("This was part of a ballot set with:"):
full = urlize_ietf_docs(full)
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 full

View file

@ -246,9 +246,6 @@ def agenda_docs(date, next_agenda):
section_key = "s"+get_doc_section(id)
if section_key not in res:
res[section_key] = []
if id.note:
# TODO: Find out why this is _here_
id.note = id.note.replace(u"\240",u"&nbsp;")
res[section_key].append({'obj':id})
return res

View file

@ -30,87 +30,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from django.utils.http import urlquote
from django.conf import settings
from django.db.models import Q
from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.contrib.auth import REDIRECT_FIELD_NAME
def passes_test_decorator(test_func, message):
"""
Decorator creator that creates a decorator for checking that user
passes the test, redirecting to login or returning a 403
error. The test function should be on the form fn(user) ->
true/false.
"""
def decorate(view_func):
def inner(request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponseRedirect('%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path())))
elif test_func(request.user):
return view_func(request, *args, **kwargs)
else:
return HttpResponseForbidden(message)
return inner
return decorate
def group_required(*group_names):
"""Decorator for views that checks that the user is logged in,
and belongs to (at least) one of the listed groups."""
return passes_test_decorator(lambda u: u.groups.filter(name__in=group_names),
"Restricted to group%s %s" % ("s" if len(group_names) != 1 else "", ",".join(group_names)))
def has_role(user, role_names):
"""Determines whether user has any of the given standard roles
given. Role names must be a list or, in case of a single value, a
string."""
if isinstance(role_names, str) or isinstance(role_names, unicode):
role_names = [ role_names ]
if not user or not user.is_authenticated():
return False
if not hasattr(user, "roles_check_cache"):
user.roles_check_cache = {}
key = frozenset(role_names)
if key not in user.roles_check_cache:
from ietf.person.models import Person
from ietf.group.models import Role
try:
person = user.get_profile()
except Person.DoesNotExist:
return False
role_qs = {
"Area Director": Q(person=person, name__in=("pre-ad", "ad"), group__type="area", group__state="active"),
"Secretariat": Q(person=person, name="secr", group__acronym="secretariat"),
"IANA": Q(person=person, name="auth", group__acronym="iana"),
"RFC Editor": Q(person=person, name="auth", group__acronym="rfceditor"),
"IAD": Q(person=person, name="admdir", group__acronym="ietf"),
"IETF Chair": Q(person=person, name="chair", group__acronym="ietf"),
"IAB Chair": Q(person=person, name="chair", group__acronym="iab"),
"WG Chair": Q(person=person,name="chair", group__type="wg", group__state="active"),
"WG Secretary": Q(person=person,name="secr", group__type="wg", group__state="active"),
}
filter_expr = Q()
for r in role_names:
filter_expr |= role_qs[r]
user.roles_check_cache[key] = bool(Role.objects.filter(filter_expr)[:1])
return user.roles_check_cache[key]
def role_required(*role_names):
"""View decorator for checking that the user is logged in and
has one of the listed roles."""
return passes_test_decorator(lambda u: has_role(u, role_names),
"Restricted to role%s %s" % ("s" if len(role_names) != 1 else "", ", ".join(role_names)))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
# overwrite group_required
group_required = lambda *group_names: role_required(*[n.replace("Area_Director", "Area Director") for n in group_names])
# REDESIGN: backwards compatibility, to be deleted
from ietf.ietfauth.utils import role_required, has_role, passes_test_decorator
group_required = lambda *group_names: role_required(*[n.replace("Area_Director", "Area Director") for n in group_names])

116
ietf/ietfauth/utils.py Normal file
View file

@ -0,0 +1,116 @@
# various authentication and authorization utilities
from django.utils.http import urlquote
from django.conf import settings
from django.db.models import Q
from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.contrib.auth import REDIRECT_FIELD_NAME
from ietf.doc.models import Document
from ietf.person.models import Person
from ietf.group.models import Role
def user_is_person(user, person):
"""Test whether user is associated with person."""
if not user.is_authenticated() or not person:
return False
if person.user_id == None:
return False
return person.user_id == user.id
def has_role(user, role_names):
"""Determines whether user has any of the given standard roles
given. Role names must be a list or, in case of a single value, a
string."""
if isinstance(role_names, str) or isinstance(role_names, unicode):
role_names = [ role_names ]
if not user or not user.is_authenticated():
return False
# use cache to avoid checking the same permissions again and again
if not hasattr(user, "roles_check_cache"):
user.roles_check_cache = {}
key = frozenset(role_names)
if key not in user.roles_check_cache:
try:
person = user.get_profile()
except Person.DoesNotExist:
return False
role_qs = {
"Area Director": Q(person=person, name__in=("pre-ad", "ad"), group__type="area", group__state="active"),
"Secretariat": Q(person=person, name="secr", group__acronym="secretariat"),
"IANA": Q(person=person, name="auth", group__acronym="iana"),
"RFC Editor": Q(person=person, name="auth", group__acronym="rfceditor"),
"IAD": Q(person=person, name="admdir", group__acronym="ietf"),
"IETF Chair": Q(person=person, name="chair", group__acronym="ietf"),
"IAB Chair": Q(person=person, name="chair", group__acronym="iab"),
"WG Chair": Q(person=person,name="chair", group__type="wg", group__state="active"),
"WG Secretary": Q(person=person,name="secr", group__type="wg", group__state="active"),
}
filter_expr = Q()
for r in role_names:
filter_expr |= role_qs[r]
user.roles_check_cache[key] = bool(Role.objects.filter(filter_expr)[:1])
return user.roles_check_cache[key]
# convenient decorator
def passes_test_decorator(test_func, message):
"""Decorator creator that creates a decorator for checking that
user passes the test, redirecting to login or returning a 403
error. The test function should be on the form fn(user) ->
true/false."""
def decorate(view_func):
def inner(request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponseRedirect('%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path())))
elif test_func(request.user):
return view_func(request, *args, **kwargs)
else:
return HttpResponseForbidden(message)
return inner
return decorate
def role_required(*role_names):
"""View decorator for checking that the user is logged in and
has one of the listed roles."""
return passes_test_decorator(lambda u: has_role(u, role_names),
"Restricted to role%s %s" % ("s" if len(role_names) != 1 else "", ", ".join(role_names)))
# specific permissions
def is_authorized_in_doc_stream(user, doc):
"""Return whether user is authorized to perform stream duties on
document."""
if has_role(user, ["Secretariat"]):
return True
if not doc.stream or not user.is_authenticated():
return False
# must be authorized in the stream or group
group_req = None
if doc.stream.slug == "ietf":
if not doc.group.type == "individ":
group_req = Q(group=doc.group)
elif doc.stream.slug == "irtf":
group_req = Q(group__acronym=doc.stream.slug) | Q(group=doc.group)
elif doc.stream.slug in ("iab", "ise"):
group_req = Q(group__acronym=doc.stream.slug)
if not group_req:
return False
return bool(Role.objects.filter(Q(name__in=("chair", "delegate", "auth"), person__user=user) & group_req))

View file

@ -6,6 +6,7 @@ from django.views.generic.simple import redirect_to
urlpatterns = patterns('ietf.ietfworkflows.views',
url(r'^(?P<name>[^/]+)/history/$', 'stream_history', name='stream_history'),
url(r'^(?P<name>[^/]+)/edit/adopt/$', 'edit_adopt', name='edit_adopt'),
# FIXME: the name edit_state is far too generic
url(r'^(?P<name>[^/]+)/edit/state/$', 'edit_state', name='edit_state'),
url(r'^(?P<name>[^/]+)/edit/stream/$', redirect_to, { 'url': '/doc/%(name)s/edit/info/'}) ,
url(r'^delegates/(?P<stream_name>[^/]+)/$', 'stream_delegates', name='stream_delegates'),

View file

@ -111,4 +111,7 @@ class Email(models.Model):
return u'"%s" <%s>' % (self.person.ascii, self.address)
else:
return self.address
def invalid_address(self):
# we have some legacy authors with unknown email addresses
return self.address.startswith("unknown-email") and "@" not in self.address

View file

@ -148,7 +148,7 @@ def perform_postREDESIGN(request, submission):
e = NewRevisionDocEvent(type="new_revision", doc=draft, rev=draft.rev)
e.time = draft.time #submission.submission_date
e.by = submitter
e.desc = "New revision available"
e.desc = "New version available: <b>%s-%s.txt</b>" % (draft.name, draft.rev)
e.save()
if draft.stream_id == "ietf" and draft.group.type_id == "wg" and draft.rev == "00":

View file

@ -195,11 +195,21 @@ def fetch_index_xml(url):
return urllib2.urlopen(url)
def parse_index(response):
def getDocList(parentNode, tagName):
def normalize_std_name(std_name):
# remove zero padding
prefix = std_name[:3]
if prefix in ("RFC", "FYI", "BCP", "STD"):
try:
return prefix + str(int(std_name[3:]))
except ValueError:
pass
return std_name
def extract_doc_list(parentNode, tagName):
l = []
for u in parentNode.getElementsByTagName(tagName):
for d in u.getElementsByTagName("doc-id"):
l.append(d.firstChild.data)
l.append(normalize_std_name(d.firstChild.data))
return l
also_list = {}
@ -209,8 +219,8 @@ def parse_index(response):
if event == pulldom.START_ELEMENT and node.tagName in ["bcp-entry", "fyi-entry", "std-entry"]:
events.expandNode(node)
node.normalize()
bcpid = get_child_text(node, "doc-id")
doclist = getDocList(node, "is-also")
bcpid = normalize_std_name(get_child_text(node, "doc-id"))
doclist = extract_doc_list(node, "is-also")
for docid in doclist:
if docid in also_list:
also_list[docid].append(bcpid)
@ -235,10 +245,10 @@ def parse_index(response):
current_status = get_child_text(node, "current-status").title()
updates = getDocList(node, "updates")
updated_by = getDocList(node, "updated-by")
obsoletes = getDocList(node, "obsoletes")
obsoleted_by = getDocList(node, "obsoleted-by")
updates = extract_doc_list(node, "updates")
updated_by = extract_doc_list(node, "updated-by")
obsoletes = extract_doc_list(node, "obsoletes")
obsoleted_by = extract_doc_list(node, "obsoleted-by")
stream = get_child_text(node, "stream")
wg = get_child_text(node, "wg_acronym")
if wg and ((wg == "NON WORKING GROUP") or len(wg) > 15):
@ -329,7 +339,7 @@ def update_docs_from_rfc_index(data, skip_older_than_date=None):
if not doc:
results.append("created document %s" % name)
doc = Document.objects.get_or_create(name=name)[0]
doc = Document.objects.create(name=name, type=DocTypeName.objects.get(slug="draft"))
# add alias
DocAlias.objects.get_or_create(name=name, document=doc)
@ -360,8 +370,11 @@ def update_docs_from_rfc_index(data, skip_older_than_date=None):
if doc.stream != stream_mapping[stream]:
changed_attributes["stream"] = stream_mapping[stream]
if not doc.group and wg:
changed_attributes["group"] = Group.objects.get(acronym=wg)
if not doc.group: # if we have no group assigned, check if RFC Editor has a suggestion
if wg:
changed_attributes["group"] = Group.objects.get(acronym=wg)
else:
changed_attributes["group"] = Group.objects.get(type="individ")
if not doc.latest_event(type="published_rfc"):
e = DocEvent(doc=doc, type="published_rfc")

View file

@ -269,7 +269,7 @@ class RFCSyncTestCase(django.test.TestCase):
self.assertEqual(rfc_published_date.month, today.month)
self.assertEqual(current_status, "Proposed Standard")
self.assertEqual(updates, ["RFC123"])
self.assertEqual(set(also), set(["BCP0001", "FYI0001", "STD0001"]))
self.assertEqual(set(also), set(["BCP1", "FYI1", "STD1"]))
self.assertEqual(draft, doc.name)
self.assertEqual(wg, doc.group.acronym)
self.assertEqual(has_errata, True)
@ -288,9 +288,9 @@ class RFCSyncTestCase(django.test.TestCase):
self.assertEqual(doc.docevent_set.all()[0].time.date(), today)
self.assertTrue("errata" in doc.tags.all().values_list("slug", flat=True))
self.assertTrue(DocAlias.objects.filter(name="rfc1234", document=doc))
self.assertTrue(DocAlias.objects.filter(name="bcp0001", document=doc))
self.assertTrue(DocAlias.objects.filter(name="fyi0001", document=doc))
self.assertTrue(DocAlias.objects.filter(name="std0001", document=doc))
self.assertTrue(DocAlias.objects.filter(name="bcp1", document=doc))
self.assertTrue(DocAlias.objects.filter(name="fyi1", document=doc))
self.assertTrue(DocAlias.objects.filter(name="std1", document=doc))
self.assertTrue(RelatedDocument.objects.filter(source=doc, target__name="rfc123", relationship="updates"))
self.assertEqual(doc.title, "A Testing RFC")
self.assertEqual(doc.abstract, "This is some interesting text.")
@ -443,6 +443,7 @@ class RFCEditorUndoTestCase(django.test.TestCase):
self.assertEquals(DeletedEvent.objects.count(), deleted_before + 1)
# delete e1
draft.state_cache = None
r = self.client.post(url, dict(event=e1.id))
self.assertEquals(draft.get_state("draft-rfceditor"), None)

View file

@ -10,13 +10,10 @@
{% if doc.type_id == "draft" or doc.type_id == "conflrev" %}
<div class="action">
{% if deferred %}
<div><a href="{% url doc_undefer_ballot name=doc.name %}">Undefer ballot</a></div>
<a href="{% url doc_undefer_ballot name=doc.name %}">Undefer ballot</a>
<div>Ballot deferred by {{ deferred.by }} on {{ deferred.time|date:"Y-m-d" }}.</div>
{% else %}
<div><a href="{% url doc_defer_ballot name=doc.name %}">Defer ballot</a></div>
{% endif %}
{% if user|has_role:"Secretariat" %}
<div><a href="{% url doc_clear_ballot name=doc.name %}">Clear ballot</a></div>
<a href="{% url doc_defer_ballot name=doc.name %}">Defer ballot</a>
{% endif %}
</div>
{% endif %}

View file

@ -0,0 +1,155 @@
{% extends "base.html" %}
{% load ietf_filters %}
{% block title %}{{ doc.canonical_name }}-{{ doc.rev }}{% endblock %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/doc.css"></link>
{% endblock %}
{% block content %}
{{ top|safe }}
<div class="snapshots">
Snapshots:
<span class="revisions">
{% for rev in revisions %}
<a {% if rev != doc.rev %}href="{% url doc_view name=doc.name %}{% if not forloop.last %}{{ rev }}/{% endif %}"{% endif %}>{{ rev }}</a>
{% endfor %}
</span>
</div>
<div class="ietf-box metabox">
<div>
{% if snapshot %}Snapshot of{% endif %}
{% if doc.get_state_slug != "approved" %}Proposed{% endif %}
Charter for "{{ group.name }}"
(<a href="{% url ietf.wginfo.views.wg_charter acronym=group.acronym %}">{{ group.acronym }}</a>) {{ group.type.name }}
</div>
<table id="metatable" width="100%">
<tr>
<td>WG State:</td>
<td>{{ group.state.name }}</td>
</tr>
<tr>
<td><a href="/doc/help/state/charter/">Charter State</a>:</td>
<td>
<div>
<a title="{{ doc.get_state.desc }}"
{% if not snapshot and user|has_role:"Area Director,Secretariat" %}
class="editlink" href="{% url charter_change_state name=doc.name %}"
{% endif %}>
{{ doc.get_state.name }}
</a>
{% if chartering == "initial" %} - (Initial Chartering){% endif %}
{% if chartering == "rechartering" %} - (Rechartering){% endif %}
</div>
{% if not snapshot and chartering %}
<div class="telechat">
<a
{% if user|has_role:"Area Director,Secretariat" %}
class="editlink" href="{% url charter_telechat_date name=doc.name %}"
{% endif %}>
{% if not telechat %}Not on agenda of IESG telechat{% else %}On agenda of {{ telechat.telechat_date|date:"Y-m-d" }} IESG telechat{% endif %}
</a>
</div>
{% if ballot_summary %}
<div class="ballot-summary">
({{ ballot_summary }})
</div>
{% endif %}
{% endif %}
</td>
</tr>
{% if chartering and group.comments %}
<tr>
{% if chartering == "initial" %}<td>Reason for chartering:</td>{% endif %}
{% if chartering == "rechartering" %}<td>Reason for rechartering:</td>{% endif %}
<td>{{ group.comments }}</td>
</tr>
{% endif %}
<tr>
<td>Responsible AD:</td>
<td><a {% if request.user|has_role:"Area Director,Secretariat" %}class="editlink" href="{% url charter_edit_ad name=doc.name %}"{% endif %}>{{ doc.ad|default:"none" }}</a> </td>
</tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
<tr>
<td>Send notices to:</td>
<td><a {% if user|has_role:"Area Director,Secretariat" %}
class="editlink" href="{% url charter_edit_notify name=doc.name %}"
{% endif %}>
{{ doc.notify|default:"none" }}
</a>
</td>
</tr>
<tr><td>Last updated:</td><td> {{ doc.time|date:"Y-m-d" }}</td></tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
</table>
<div class="links">
<a href="/feed/group-changes/{{ group.acronym }}/">Atom feed</a>
</div>
<div class="actions">
{% if not snapshot and user|has_role:"Area Director,Secretariat" %}
{% if chartering %}
{% url charter_startstop_process name=doc.name option='abandon' as abandon_url %}{% if abandon_url %}<a class="button" href="{{ abandon_url }}">Abandon Effort</a>{% endif %}
{% if request.user|has_role:"Secretariat" %}
{% url charter_approve name=doc.name as approve_url %}{% if approve_url %}<a class="button" href="{{ approve_url }}">Approve Charter</a>{% endif %}
{% endif %}
{% else %}
{% if group.state_id == "proposed" or group.state_id == "bof" %}
{% url charter_submit name=doc.name option='initcharter' as start_url %}{% if start_url %}<a class="button" href="{{ start_url }}">Start Chartering</a>{% endif %}
{% else %}
{% url charter_submit name=doc.name option='recharter' as recharter_url %}{% if recharter_url %}<a class="button" href="{{ recharter_url }}">Recharter</a>{% endif %}
{% endif %}
{% endif %}
{% endif %}
</div>
</div>
<p>Other versions: <a href="{{ txt_url }}">plain text</a></p>
<h3>Charter {{ doc.canonical_name }}-{{ doc.rev }}
{% if not snapshot and user|has_role:"Area Director,Secretariat" and chartering and group.state_id != "conclude" %}
<a class="edit" href="{% url charter_submit name=doc.name %}">Change charter text</a>
{% endif %}
</h3>
{% if doc.rev != "" %}
<div class="document-markup">
{{ content|safe|keep_spacing|sanitize_html|wordwrap:80|safe }}
</div>
{% endif %}
{% if not snapshot and chartering %}
<h3>Proposed Milestones
{% if user|has_role:"Area Director,Secretariat" %}
<a class="edit" href="{% url wg_edit_charter_milestones acronym=doc.group.acronym %}">Edit charter milestones</a>
{% endif %}
</h3>
{% if milestones %}
{% include "wginfo/milestones.html" %}
{% else %}
<p>No milestones for charter found.</p>
{% endif %}
{% endif %}
{% endblock %}

View file

@ -1,8 +1,8 @@
{% extends "idrfc/doc_main.html" %}
{% extends "base.html" %}
{% load ietf_filters %}
{% block title %}{{ doc.name }}-{{ doc.rev }}{% endblock %}
{% block title %}{{ doc.canonical_name }}-{{ doc.rev }}{% endblock %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/doc.css"></link>
@ -24,7 +24,7 @@
<div>
{% if snapshot %}Snapshot of{% endif %}
{% if doc.get_state_slug not in approved_states %}Proposed{% endif %}
IESG Conflict Review for the {{conflictdoc.stream}} document: <a href="{% url doc_view name=conflictdoc.canonical_name %}">{{ conflictdoc.canonical_name }}{% if conflictdoc.get_state_slug != 'rfc' %}-{{ conflictdoc.rev }}{% endif %}</a>
IESG Conflict Review for the {{conflictdoc.stream}} document: <a href="{% url doc_view name=conflictdoc.canonical_name %}">{{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}</a>
</div>
<table id="metatable" width="100%">
@ -42,26 +42,25 @@
{% endif %}
</div>
</td>
</tr>
{% if not snapshot %}
<tr>
<td>Telechat Date:</td>
<td>
{% if not snapshot %}
<div class="telechat">
<a {% if not snapshot and user|has_role:"Area Director,Secretariat" and doc.get_state_slug not in approved_states %}
class="editlink" href="{% url conflict_review_telechat_date name=doc.name %}"
{%endif%} >
{% if not telechat %}Not on agenda of an IESG telechat{% else %}On agenda of {{ telechat.telechat_date|date:"Y-m-d" }} IESG telechat{% if doc.returning_item %} (returning item){% endif %}{% endif %}
</a>
</div>
{% if ballot_summary %}
<div class="ballot-summary">
({{ ballot_summary }})
</div>
{% endif %}
{% endif %}
</td>
</tr>
{% endif %}
<tr>
<td>Shepherding AD:</td>
@ -100,7 +99,7 @@
</div>
<h3>Conflict Review for {{ conflictdoc.name }}-{{ conflictdoc.rev }}
<h3>Conflict Review for {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}
{% if not snapshot and user|has_role:"Area Director,Secretariat" and doc.get_state_slug != 'apprsent' %}
<a class="edit" href="{% url conflict_review_submit name=doc.name %}">Change conflict review text</a>
@ -108,7 +107,7 @@
</h3>
{% if doc.rev %}
<div class="markup_draft">
<div class="document-markup">
{{ content|fill:"80"|safe|linebreaksbr|keep_spacing|sanitize_html|safe }}
</div>
{% endif %}

View file

@ -0,0 +1,284 @@
{% extends "base.html" %}
{% load ietf_filters %}
{% block title %}{% if doc.get_state_slug == "rfc" %}RFC {{ rfc_number }}{% else %}{{ name }}-{{ doc.rev }}{% endif %}{% endblock %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/doc.css"></link>
<meta name="description" content="{{ doc.title }} {% if doc.get_state_slug == "rfc" %}(RFC {{ rfc_number }}{% if published %}, {{ published.time|date:"F Y" }}{% endif %}{% if obsoleted_by %}; obsoleted by {{ obsoleted_by|join:", " }}{% endif %}){% else %}(Internet-Draft, {{ doc.time|date:"Y" }}){% endif %}" />
{% endblock %}
{% block content %}
{{ top|safe }}
<div class="ietf-box metabox">
<table id="metatable" width="100%">
<tr>
<td>Document type:</td>
<td>
{% if doc.get_state_slug == "rfc" %}
RFC - {{ doc.std_level }}
({% if published %}{{ published.time|date:"F Y" }}{% else %}publication date unknown{% endif %}{% if has_errata %}; <a href="http://www.rfc-editor.org/errata_search.php?rfc={{ rfc_number }}" rel="nofollow">Errata</a>{% endif %})
{% if obsoleted_by %}<div>Obsoleted by {{ obsoleted_by|join:", "|urlize_ietf_docs }}</div>{% endif %}
{% if updated_by %}<div>Updated by {{ updated_by|join:", "|urlize_ietf_docs }}</div>{% endif %}
{% if obsoletes %}<div>Obsoletes {{ obsoletes|join:", "|urlize_ietf_docs }}</div>{% endif %}
{% if updates %}<div>Updates {{ updates|join:", "|urlize_ietf_docs }}</div>{% endif %}
{% if rfc_aliases %}<div>Also Known As {{ rfc_aliases|join:", "|urlize_ietf_docs }}</div>{% endif %}
{% if draft_name %}<div>Was <a href="/doc/{{ draft_name}}/">{{ draft_name }}</a> {% if submission %}({{ submission|safe }}){% endif %}</div>{% endif %}
{% else %}
{{ doc.get_state }} Internet-Draft {% if submission %}({{ submission|safe }}){% endif %}
{% if resurrected_by %}- resurrect requested by {{ resurrected_by }}{% endif %}
{% endif %}
{% if replaces %}<div>Replaces: {{ replaces|join:", "|urlize_ietf_docs }}</div>{% endif %}
{% if replaced_by %}<div>Replaced by: {{ replaced_by|join:", "|urlize_ietf_docs }}</div>{% endif %}
</td>
</tr>
<tr>
<td>Document stream:</td>
<td>
<a {% if can_change_stream %} class="editlink" href="{% url doc_change_stream name=doc.name %}"{% endif %}>
{{ doc.stream|default:"No stream defined" }}
</a>
</td>
</tr>
<tr>
<td>Last updated:</td>
<td>
{{ doc.time|date:"Y-m-d" }}
{% if latest_revision and latest_revision.time.date != doc.time.date %}
(latest revision {{ latest_revision.time|date:"Y-m-d" }})
{% endif %}
</td>
</tr>
{% if doc.get_state_slug != "rfc" %}
<tr>
<td>Intended RFC status:</td>
<td>
<a {% if can_edit or can_edit_stream_info %}class="editlink" href="{% url doc_change_intended_status name=doc.name %}"{% endif %}>
{{ doc.intended_std_level|default:"Unknown" }}</a>
</td>
</tr>
{% endif %}
<tr>
<td>Other versions:</td>
<td>
{% if doc.get_state_slug != "active" and doc.get_state_slug != "rfc" %}(expired, archived):{% endif %}
{% if file_urls %}
{% for label, url in file_urls %}<a href="{{ url }}">{{ label}}</a>{% if not forloop.last%}, {% endif %}{% endfor %}
{% else %}
(not online)
{% endif %}
</td>
</tr>
{% if conflict_reviews %}
<tr>
<td>IETF Conflict Review:</td>
<td>{{ conflict_reviews|join:", "|urlize_ietf_docs }}</td>
</tr>
{% endif %}
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
<tr>
<td>{{ doc.stream }} State:</td>
<td class="stream-state">
<a {% if can_edit_stream_info %}class="editlink" href="{% url edit_state name=doc.name %}"{% endif %}>
{{ stream_state|default:"(None)" }}
</a>
{% for m in milestones %}
<span title="{{ m.desc }} ({{ m.group.acronym }} milestone)" class="milestone">{{ m.due|date:"M Y" }}</span>
{% endfor %}
{% if stream_tags %}
<div class="stream-tags">{% for tag in stream_tags %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}</div>
{% endif %}
</td>
</tr>
{% if consensus %}
<tr>
<td>Consensus:</td>
<td>
<a title="Whether the document is the result of a community consensus process as defined in RFC 5741" {% if can_edit or can_edit_stream_info %}class="editlink" href="{% url doc_edit_consensus name=doc.name %}"{% endif %}>
{{ consensus }}
</a>
</td>
</tr>
{% endif %}
<tr>
<td>Document shepherd:</td>
<td>
{# the shepherd edit page only works for WGs at the moment ... #}
<a {% if can_edit_stream_info and group.type_id == "wg" %}class="editlink" href="{% url doc_managing_shepherd acronym=group.acronym,name=doc.name %}"{% endif %}>
{{ doc.shepherd|default:"No shepherd assigned" }}
</a>
</td>
</tr>
{# the shepherd write up page only works for WGs at the moment ... #}
{% if group.type_id == "wg" %}
{% if shepherd_writeup or can_edit_shepherd_writeup %}
<tr>
<td>Shepherd Write-Up:</td>
<td>
<a {% if can_edit_shepherd_writeup %}class="editlink"{% endif %} href="{% url doc_managing_writeup acronym=group.acronym,name=doc.name %}">
{% if shepherd_writeup %}Last changed {{ shepherd_writeup.time|date:"Y-m-d"}}{% else %}(None){% endif %}
</a>
</td>
</tr>
{% endif %}
{% endif %}
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
{% if published and started_iesg_process and published.time < started_iesg_process.time %}
<tr>
<td colspan='2' style="font-style:italic">This information refers to IESG processing after the RFC was initially published:</td>
</tr>
{% endif %}
<tr>
<td><a href="/idtracker/help/state/">IESG State</a>:</td>
<td>
<a {% if iesg_state and can_edit %}class="editlink" href="{% url doc_change_state name=doc.name %}"{% endif %}>
{{ iesg_state|default:"I-D Exists" }}</a>
{% if iana_review_state %}
<div>IANA Review State:
<a {% if can_edit_iana_state %}class="editlink" href="{% url doc_change_iana_state name=doc.name state_type="iana-review" %}"{% endif %}>{{ iana_review_state }}</a>
</div>
{% endif %}
{% if iana_review_state %}
<div>IANA Action State:
<a {% if can_edit_iana_state %}class="editlink" href="{% url doc_change_iana_state name=doc.name state_type="iana-action" %}"{% endif %}>{{ iana_action_state }}</a>
</div>
{% endif %}
{% if rfc_editor_state %}
<div>
RFC Editor State:
<a href="http://www.rfc-editor.org/queue2.html#{{ doc.name }}">{{ rfc_editor_state }}</a></div>
{% endif %}
<div class="telechat">
<a {% if can_edit %}class="editlink" href="{% url doc_change_telechat_date name=doc.name %}"{% endif %}>
{% if telechat %}
On agenda of {{ telechat.telechat_date }} IESG telechat {% if telechat.returning_item %} (returning item){% endif %}
{% else %}
{% if can_edit %}Not on an upcoming telechat agenda{% endif %}
{% endif %}
</a>
</div>
{% if ballot_summary %}<div class="ballot-summary">({{ ballot_summary }})</div>{% endif %}
</td>
</tr>
<tr>
<td>Responsible AD:</td>
<td>
<a {% if can_edit %}class="editlink" href="{% url doc_change_ad name=doc.name %}"{% endif %}>
{{ doc.ad|default:"(None)" }}
</a>
</td>
</tr>
{% if iesg_state %}
{% if doc.note or can_edit %}
<tr>
<td>IESG Note:</td>
<td>
<a {% if can_edit %}class="editlink" href="{% url doc_change_iesg_note name=doc.name %}"{% endif %}>
{{ doc.note|default:"(None)"|linebreaksbr }}
</a>
</td>
</tr>
{% endif %}
{% endif %}
<tr>
<td>Send notices to:</td>
<td>
<a {% if can_edit %}class="editlink" href="{% url doc_change_notify name=doc.name %}"{% endif %}>
{{ doc.notify|default:"No addresses provided"}}
</a>
</td>
</tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
</table>
<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="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="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>
{% endif %}
</div>
{% if can_edit and iesg_state %}
<div class="links">
<a href="{% url doc_ballot_lastcall name=doc.name %}">Last Call Text</a>
| <a href="{% url doc_ballot_writeupnotes name=doc.name %}">Ballot Text</a>
| <a href="{% url doc_ballot_approvaltext name=doc.name %}">Announcement Text</a>
</div>
{% endif %}
{% if actions %}
<div class="actions">
{% for label, url in actions %}<a class="button" href="{{ url }}">{{ label }}</a> {% endfor %}
</div>
{% endif %}
</div>
{% if doc.get_state_slug == "active" or doc.get_state_slug == "rfc" %}
<div class="document-markup">
{{ content|safe }}
</div>
{% if split_content %}
<p><a style="display:inline-block;margin-left:17em;" href="?include_text=1">[include full document text]</a></p>
{% endif %}
{% else %}
<p>This Internet-Draft is no longer active. Unofficial copies of old Internet-Drafts can be found here:<br/>
<a href="http://tools.ietf.org/id/{{ doc.name }}">http://tools.ietf.org/id/{{ doc.name }}</a></p>
<h4>Abstract</h4>
<p>{{ doc.abstract|escape }}</p>
<h4>Authors</h4>
<p>
{% for author in doc.documentauthor_set.all %}
<a {% if not author.author.invalid_address %}href="mailto:{{ author.author.address }}"{% endif %}>{{ author.author.person }} {% if not author.author.invalid_address %}&lt;{{ author.author.address }}&gt;{% endif %}</a>
{% if not forloop.last %}<br/>{% endif %}{% endfor %}
</p>
<p>(Note: The e-mail addresses provided for the authors of this Internet-Draft may no longer be valid)</p>
{% endif %}
{% endblock %}

View file

@ -6,13 +6,14 @@
{% 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 }}/" />
{% endblock %}
{% block content %}
{{ top|safe }}
{% if diff_revisions and diff_revisions|length > 1 %}
<div class="ietf-box diffTool">
<div class="ietf-box diff-tool">
<h2>Diffs</h2>
<form action="http{% if request.is_secure %}s{% endif %}:{{rfcdiff_prefix}}" method="get" target="_blank">
@ -55,7 +56,7 @@
<h2>Document history</h2>
{% if user|has_role:"Area Director,Secretariat,IANA,RFC Editor" %}
<div class="history-actions">
<a href="{% url doc_add_comment name=doc.name %}">Add comment</a>
<a class="button" href="{% url doc_add_comment name=doc.name %}">Add comment</a>
</div>
{% endif %}

View file

@ -9,16 +9,20 @@
{% block content %}
{{ top|safe }}
{% for title, text, url in writeups %}
<div class="writeup">
{% for title, subtitle, writeups in sections %}
<h2>{{ title }}</h2>
{% if can_edit %}<a href="{{ url }}" class="edit">{% if text %}Edit{% else %}Generate{% endif %} {{ title }}</a>{% endif %}
{% if subtitle %}<p>{{ subtitle|safe }}</p>{% endif %}
{% for name, text, url in writeups %}
<div class="writeup">
{% if can_edit %}<a href="{{ url }}" class="edit button">{% if text %}Edit{% else %}Generate{% endif %} {{ name }}</a>{% endif %}
<pre {% if can_edit %}class="editable"{% endif %}>
{{ text }}
</pre>
</div>
{% endfor%}
{% endfor %}
{% endfor %}
{% endblock content %}

View file

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block morecss %}
form #id_content {
width: 40em;
height: 450px;
}
{% endblock %}
{% block title %}
Shepherd writeup for {{ doc.canonical_name }}-{{ doc.rev }}
{% endblock %}
{% block content %}
<h1>Shepherd writeup for {{ doc.canonical_name }}-{{ doc.rev }}</h1>
<pre style="border:1px solid black;padding:5px;">{{writeup}}</pre>
<a href="{% url doc_view name=doc.name %}">Back</a>
{% if can_edit %}
<span id="doc_edit_shepherd_writeup" class="yui-button yui-link-button" style="margin-left:2px;">{% url doc_edit_shepherd_writeup name=doc.name as doc_edit_url %}{% if doc_edit_url %}<span class="first-child"><a href="{{doc_edit_url}}">Edit</a></span>{% endif %}</span>
{% endif %}
{% endblock %}

View file

@ -1,2 +0,0 @@
#Discuss Yes No Objection Abstain Recuse No Record
{% for p in ballot.get_discuss %}{{p.ad_name}}, {% endfor %} {% for p in ballot.get_yes %}{{p.ad_name}}, {% endfor %} {% for p in ballot.get_no_objection %}{{p.ad_name}}, {% endfor %} {% for p in ballot.get_abstain %}{{p.ad_name}}, {% endfor %} {% for p in ballot.get_recuse %}{{p.ad_name}}, {% endfor %} {% for p in ballot.get_no_record %}{{p.ad_name}}, {% endfor %}
Can't render this file because it has a wrong number of fields in line 2.

View file

@ -1,37 +0,0 @@
{% 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 %}<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>

View file

@ -1,96 +0,0 @@
{% 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 %}
<table class="ietf-ballot"><tr valign="top"><td class="left">
{% if doc_ballot_edit_button and user|in_group:"Area_Director,Secretariat" %}
{% if user|in_group:"Area_Director" %}
<div style="margin-top:8px; margin-bottom:8px;"><span id="doc_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url idrfc.views_ballot.edit_position name=doc.name,ballot_id=ballot.pk %}">Edit position</a></span></span></div>
{% endif %}
{% if doc.active_defer_event %}
<div style="margin-top:8px; margin-bottom:8px;"><span id="doc_undefer_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url doc_undefer_ballot name=doc.name,ballot_id=ballot.pk %}">Undefer ballot</a></span></span></div>
<div style="margin-top:8px; margin-bottom:8px;">Ballot deferred by {{ doc.active_defer_event.by }} on {{ doc.active_defer_event.time|date:"Y-m-d" }}.</div>
{% else %}
<div style="margin-top:8px; margin-bottom:8px;"><span id="doc_defer_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url doc_defer_ballot name=doc.name,ballot_id=ballot.pk %}">Defer ballot</a></span></span></div>
{% endif %}
{% endif %}
{% if doc_ballot_edit_button and user|in_group:"Secretariat" %}
<div style="margin-top:8px; margin-bottom:8px;"><span id="doc_clear_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url doc_clear_ballot name=doc.name,ballot_id=ballot.pk %}">Clear ballot</a></span></span></div>
{% endif %}
<p style="margin-top:1em;"><span class="square" style="background:#c00000;"></span><b>Discuss</b><br/>
{% with bw.get_discuss as positions %}{% include "idrfc/doc_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:#80ff80;"></span><b>Yes</b><br/>
{% with bw.get_yes as positions %}{% include "idrfc/doc_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:#80ff80;"></span><b>No Objection</b><br/>
{% with bw.get_no_objection as positions %}{% include "idrfc/doc_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:#ffff00;"></span><b>Abstain</b><br/>
{% with bw.get_abstain as positions %}{% include "idrfc/doc_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:#c0c0c0;"></span><b>Recuse</b><br/>
{% with bw.get_recuse as positions %}{% include "idrfc/doc_ballot_list.html" %}{% endwith %}</p>
<p><span class="square" style="background:white;"></span><b>No Record</b><br/>
{% with bw.get_no_record as positions %}{% include "idrfc/doc_ballot_list.html" %}{% endwith %}</p>
</td>
<td class="right">
<h2 style="margin-top:12px;">Discusses and other comments</h2>
{% if bw.is_ballot_set %}<p>Other documents in this ballot set: {% for x in bw.ballot_set_other%}{{x|urlize_ietf_docs}}{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
{% if dw.in_ietf_process and dw.ietf_process.has_active_iesg_ballot %}
<p>Summary: <i>{{ dw.ietf_process.iesg_ballot_needed }}</i></p>
{% endif %}
{% for pos in bw.get_texts|dictsort:"is_old_ad" %}
<h2 class="ballot_ad"><a name="{{pos.ad_name|slugify}}">{% if pos.is_old_ad %}[{%endif%}{{pos.ad_name|escape}}{% if pos.is_old_ad %}]{%endif%}</a></h2>
{% ifequal pos.position "Discuss" %}
<p><b>Discuss ({{pos.discuss_date}})</b> <img src="/images/comment.png" width="14" height="12" alt=""/></p>
<pre>{{pos.discuss_text|wrap_text:80|escape }}</pre>
{% endifequal %}
{% if pos.comment_text %}
<p><b>Comment ({{pos.comment_date}})</b> <img src="/images/comment.png" width="14" height="12" alt=""/></p>
<pre>{{pos.comment_text|wrap_text:80|escape }}</pre>
{% endif %}
{% endfor %}
</td></tr></table>

View file

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

View file

@ -1,6 +0,0 @@
{{ doc.title }}
{% if info.is_rfc %}
({{doc.maturity_level}}, {{doc.publication_date|date:"Y"}}){% if doc.obsoleted_by %}; Obsoleted by {{doc.obsoleted_by}}{% endif %}
{% else %}
({{info.type}}; {{doc.publication_date|date:"Y"}})
{% endif %}

View file

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

View file

@ -1,80 +0,0 @@
{% 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 %}
<table class="ietf-table">
<tr><th class="comment_date">Date</th><th>Version</th><th>By</th><th>Text</th></tr>
{% for c in history %}
<tr class="{% cycle oddrow,evenrow %}"{% if c.is_com %} id="history-{{c.comment.id }}"{% endif %}>
<td class="comment_date">{{ c.date|date:"Y-m-d" }}</td>
{% if c.is_rev %}
<td>{{ c.revision }}</td>
<td>(System)</td>
<td>New version available: <a href="http://tools.ietf.org/id/{{c.draft_name}}-{{c.revision}}.txt">{{c.draft_name}}-{{c.revision}}</a> {% ifnotequal c.revision "00" %}(<a href="http:{{rfcdiff_prefix}}?url2={{c.draft_name}}-{{c.revision}}">diff from -{{c.revision|add:"-1"|stringformat:"02d"}}</a>){% endifnotequal %}</td>
{% endif %}
{% if c.is_text %}
<td>&nbsp;</td>
<td>(System)</td>
<td>{{ c.text|safe }}</td>
{% endif %}
{% if c.is_com %}
<td>{{ c.comment.version }}</td>
<td>{{ c.info.by|escape }}</td>
<td>{% if c.comment.ballot %}
[Ballot {{ c.comment.get_ballot_display }}]<br />
{% endif %}
{% if c.info.snipped %}
<div id="commentS{{c.comment.id}}">{{ c.info.textSnippet|safe|sanitize_html|safe }}</div>
<span class="comment_toggle" onclick="toggleComment({{c.comment.id}})" id="commentT{{c.comment.id}}">[show all]</span>
<div id="commentF{{c.comment.id}}" style="display:none;">
{{ c.info.text|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
</div>
{% else %}
{% if c.info.dontmolest %}
{{ c.info.text|safe }}
{% else %}
{{ c.info.text|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
{% endif %}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</table>

View file

@ -1,126 +0,0 @@
{% extends "base.html" %}
{% 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 %}
{% block morecss %}
.metabox { width: 99%; margin-top:8px; padding:4px; margin-bottom:1em; }
#metatable { border: 0; border-spacing: 0; }
#metatable tr { vertical-align:top ;}
#metatable .post-rfc { font-style: italic; color: #004000; }
.comment_toggle { text-decoration: underline; color: blue; }
.comment_date { white-space: nowrap; }
div.diffTool { padding: 8px 4px; margin: 8px 0;}
.diffTool label { float:left; width:50px; }
.markup_draft pre {line-height: 1.2em; margin: 0; }
.m_hdr, .m_ftr { color: #808080; }
.m_ftr { border-bottom: 1px solid #a0a0a0; }
.m_h { font-family: arial; font-weight:bold;}
a.editlink {
background-image: url("/images/pencil.png");
background-size:10px;
background-position: right top;
background-attachment: scroll;
background-repeat: no-repeat;
padding-right: 12px;
}
a.editlink:link {text-decoration:none; color:inherit;}
a.editlink:visited {text-decoration:none; color:inherit;}
a.editlink:hover {text-decoration:underline;}
a.editlink:active {text-decoration:underline;}
{% endblock %}
{% block pagehead %}
{% if doc.in_ietf_process %}
<link rel="alternate" type="application/atom+xml" href="/feed/comments/{% if info.is_rfc %}rfc{{doc.rfc_number}}{% else %}{{doc.draft_name}}{% endif %}/" />
{% endif %}
<meta name="description" content="{% include "idrfc/doc_description.html" %}" />
{% comment %}
## See ticket #545 -- this is commented out because the versions
## that we currently serve is not sufficiently cachable, so the
## prefetched version is actually thrown away.
## Once the content becomes cacheable, these links should
## be reinserted into the document.
{% ifequal tab "document" %}
{% if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot %}
<link rel="prefetch" href="ballot/" />
<link rel="prefetch" href="writeup/" />
{% endif %}
<link rel="prefetch" href="history/" />
{% endifequal %}
{% endcomment %}
{% endblock %}
{% block title %}{% include "idrfc/doc_title.html" %}{% endblock title %}
{% block content %}
<h1>{{ doc.title }}<br/>{% include "idrfc/doc_title.html" %}</h1>
<div id="mytabs" class="yui-navset">
<ul class="yui-nav">
<li{% ifequal tab "document" %} class="selected"{% endifequal %}><a href="{{ doc.get_absolute_url }}"><em>Document</em></a></li>
<li{% if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot %}{% ifequal tab "ballot" %} class="selected"{% endifequal %}{%else%} class="disabled"{%endif%}><a href="{{ doc.get_absolute_url }}ballot/"><em>IESG Evaluation Record</em></a></li>
<li{% if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot %}{% ifequal tab "writeup" %} class="selected"{% endifequal %}{%else%} class="disabled"{%endif%}><a href="{{ doc.get_absolute_url }}writeup/"><em>IESG Writeups</em></a></li>
<li{% ifequal tab "history" %} class="selected"{% endifequal %}><a href="{{ doc.get_absolute_url }}history/"><em>History</em></a></li>
</ul>
<div class="yui-content">
{% block tab_content %}{% endblock %}
</div> <!-- yui-content -->
</div> <!-- mytabs -->
{% endblock content %}
{% block scripts %}
function toggleComment(n) {
var el = document.getElementById("commentF"+n);
var el2 = document.getElementById("commentS"+n);
var el3 = document.getElementById("commentT"+n);
if (el.style.display == 'none') {
el.style.display = 'block';
el2.style.display = 'none';
el3.innerHTML = ""; //[hide]";
} else {
el.style.display = 'none';
el2.style.display= 'block';
el3.innerHTML = "[show all]";
}
}
{% endblock scripts %}

View file

@ -1,47 +0,0 @@
{% extends "idrfc/doc_main.html" %}
{% 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 %}
{% block tab_content %}
{% if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot %}
{% with 1 as doc_ballot_edit_button %}
{% with doc.ietf_process.iesg_ballot as ballot %}
{% include "idrfc/doc_ballot.html" %}
{% endwith %}
{% endwith %}
{% endif %}
{% endblock tab_content %}

View file

@ -1,91 +0,0 @@
{% extends "idrfc/doc_main.html" %}
{% 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 %}
{% block tab_content %}
<div class="ietf-box metabox">
<table id="metatable" width="100%">
{% block doc_metatable %}{% endblock %}
</table>
<div style="padding-top:6px;padding-bottom:6px;padding-left:2px;">
{% block doc_metalinks %}{% endblock %}
</div>
{% block doc_metabuttons %}
{% if user|in_group:"Area_Director,Secretariat" %}
<div style="padding-bottom:2px;">
{% ifequal doc.draft_status "Expired" %}
{% if not doc.resurrect_requested_by and user|in_group:"Area_Director" %}
<span id="doc_request_resurrect_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url doc_request_resurrect name=doc.draft_name %}">Request resurrect</a></span></span>
{% endif %}
{% if user|in_group:"Secretariat" %}
<span id="doc_resurrect_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url doc_resurrect name=doc.draft_name %}">Resurrect</a></span></span>
{% endif %}
{% else %} {# not expired #}
{% if stream_info.stream.name == 'ISE' or stream_info.stream.name == 'IRTF' %}
{% if user|in_group:"Secretariat" and not info.conflict_reviews %}
<span id="doc_conflict_review_button" class="yui-button yui-link-button" style="margin-left:2px;">{% url conflict_review_start name=doc.draft_name as start_review_url %}{% if start_review_url %}<span class="first-child"><a href="{{start_review_url}}">Begin IETF Conflict Review {% if not doc.underlying_document.intended_std_level %}(note that intended status is not set){% endif %}</a></span>{% endif %}</span>
{% endif %}
{% else %} {# Not a candidate for conflict review #}
{% if stream_info.stream.name == 'IETF' and not doc.in_ietf_process %}
<span id="doc_add_button" class="yui-button yui-link-button" style="margin-left:2px;">{% url doc_edit_info name=doc.draft_name as doc_edit_url %}{% if doc_edit_url %}<span class="first-child"><a href="{{doc_edit_url}}">Begin IESG Processing</a></span>{% endif %}</span>
{% else %}
{% if doc.underlying_document.get_state_slug == 'rfc' %}
<span id="doc_start_status_change" class="yui-button yui-link-button" style="margin-left:2px;">{% url start_rfc_status_change name=doc.underlying_document.canonical_name as start_url %}{% if start_url %}<span class="first-child"><a href="{{start_url}}">Start {% if info.proposed_status_changes %}An Additional {% endif %}Status Change</a></span>{% endif %}</span>
{% endif %}
{% endif %}
{% endif %}
{% endifequal %}
</div>
{% endif %}{# if user in group #}
{% endblock doc_metabuttons %}
</div> <!-- metabox -->
<div id="rfcText1">
{% block doc_text1 %}{% endblock %}
</div> <!-- rfcText1 -->
{% endblock tab_content %}
{% block content_end %}
<div id="rfcText2">
{% if include_text %}
{% block doc_text2 %}{% endblock %}
{% else %}
<a href="?include_text=1">[include full document text]</a>
{% endif %}
</div>
{% endblock content_end %}

View file

@ -1,238 +0,0 @@
{% extends "idrfc/doc_tab_document.html" %}
{% 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 %}
{% block doc_metatable %}
<tr><td colspan='2'>{{ doc.draft_status }} {% ifnotequal doc.draft_status "RFC" %}Internet-Draft ({{ doc.submission|safe }}){% endifnotequal %}
{% ifequal doc.draft_status "Expired" %}
{% if doc.resurrect_requested_by %}(resurrect requested by {{ doc.resurrect_requested_by }}){% endif %}
{% endifequal %} </td>
<tr><td>Document Stream:</td><td>
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
{% if add_link %}<a class="editlink" href="{% url doc_change_stream name=doc.draft_name %}">{% endif %}
{% if stream_info.stream %}{{ stream_info.stream.name|default:"No stream defined" }}{% else %}No stream defined{% endif %}
{% if add_link %}</a>{% endif %}
{% endwith %}
</td></tr>
<tr><td>Last updated:</td><td> {{ doc.publication_date|default:"(data missing)" }}</td></tr>
{% with doc.replaces as r %}{% if r %}<tr><td>Replaces:</td><td> {% filter urlize_ietf_docs %}{{ r|join:", "}}{% endfilter %}</td></tr>{% endif %}{% endwith %}
{% with doc.replaced_by as r %}{% if r %}<tr><td>Replaced by:</td><td> {% filter urlize_ietf_docs %}{{ r|join:", "}}{% endfilter %}</td></tr>{% endif %}{% endwith %}
{% with info.conflict_reviews as r %}{% if r %}<tr><td>IETF Conflict Review:</td><td> {% filter urlize_ietf_docs %}{{ r|join:","}}{% endfilter %}</td></tr>{% endif %} {% endwith %}
<tr><td>Intended RFC status:</td><td>
<a{% if info.can_edit_intended_std_level %} class="editlink" href="{% url doc_change_intended_status name=doc.draft_name %}"{% endif %}>
{{ doc.underlying_document.intended_std_level|default:"(None)" }}
</a>
</td></tr>
<tr><td>Other versions:</td>
{% ifequal doc.draft_status "Active" %}
<td>
<a href="http://www.ietf.org/id/{{doc.draft_name_and_revision}}.txt">plain text</a>,
{% for ext in doc.file_types %}
{% ifnotequal ext ".txt" %}
<a href="http://www.ietf.org/id/{{doc.draft_name_and_revision}}{{ext}}">{{ext|cut:"."}}</a>,
{% endifnotequal %}
{% endfor %}
{% if not info.has_pdf %}
<a href="http://tools.ietf.org/pdf/{{doc.draft_name_and_revision}}.pdf">pdf</a>,
{% endif %}
<a href="http://tools.ietf.org/html/{{doc.draft_name_and_revision}}">html</a>
</td>
{% else %}
<td>
(expired, archived):
<a href="http://tools.ietf.org/id/{{doc.draft_name_and_revision}}.txt">plain text</a>,
{% for ext in doc.file_types %}
{% ifnotequal ext ".txt" %}
<a href="http://tools.ietf.org/id/{{doc.draft_name_and_revision}}{{ext}}">{{ext|cut:"."}}</a>,
{% endifnotequal %}
{% endfor %}
{% if not info.has_pdf %}
<a href="http://tools.ietf.org/pdf/{{doc.draft_name_and_revision}}.pdf">pdf</a>,
{% endif %}
<a href="http://tools.ietf.org/html/{{doc.draft_name_and_revision}}">html</a>
</td>
</tr>
{% endifequal %}
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
{% if stream_info.state %}
{% ifequal stream_info.stream.name "IETF" %}
<tr>
<td>IETF State:</td>
<td class="stream-state">{{ stream_info.state.name }} ({{ stream_info.streamed.get_group }})
{% if stream_info.tags %}<br /><i>{% for tag in stream_info.tags %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}</i>{% endif %}
{% if milestones %}{% for m in milestones %}<span title="In {{ m.group.acronym }} milestone: {{ m.desc }}" class="milestone">{{ m.due|date:"M Y" }}</span>{% endfor %}{% endif %}
</td>
</tr>
{% else %}
{% if stream_info.stream %}
<tr>
<td>{{ stream_info.stream.name }} status:</td>
<td>
{{ stream_info.state.name }} {% if stream_info.streamed.get_group %}({{ stream_info.streamed.get_group }}) {% endif %}
{% if stream_info.tags %}<br /><i>{% for tag in stream_info.tags %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}</i>{% endif %}
</td>
</tr>
{% endif %}
{% endifequal %}
{% endif %}
<tr><td>Document shepherd:</td><td><a {% if info.can_edit_shepherd %}class="editlink" href="{% url doc_edit_shepherd name=doc.draft_name %}"{% endif %}>{{ doc.underlying_document.shepherd|default:"(None)" }}</a></td></tr>
<tr><td></td><td><a href="{% url doc_shepherd_writeup name=doc.draft_name %}">Shepherd writeup</a></td></tr>
{% if info.consensus %}
<tr><td>Consensus:</td><td><a title="Whether the document is the result of a community consensus process as defined in RFC 5741" {% if info.can_edit_consensus %}class="editlink" href="{% url doc_edit_consensus name=doc.draft_name %}"{% endif %}>{{ info.consensus }}</a></td></tr>
{% endif %}
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
<tr><td><a href="/idtracker/help/state/">IESG State</>:</td><td>
{% if doc.in_ietf_process and user|in_group:"Area_Director,Secretariat" %}
<a class="editlink" href="{% url doc_change_state name=doc.draft_name %}"> {{ doc.friendly_state|safe }} </a>
{% else %}
{{ doc.friendly_state|safe }}
{% endif %}
{% if info.iana_review_state %}<br />IANA Review State: <a href="/help/state/draft/iana-review/">{{ info.iana_review_state.name|escape }}</a><a class="editlink" {% if user|in_group:"Secretariat,IANA" %}href="{% url doc_change_iana_state name=doc.draft_name state_type="iana-review" %}"{% endif %}>&nbsp;</a>{% endif %}
{% if info.iana_action_state %}<br />IANA Action State: <a href="/help/state/draft/iana-action/">{{ info.iana_action_state.name|escape }}</a><a class="editlink" {% if user|in_group:"Secretariat,IANA" %}href="{% url doc_change_iana_state name=doc.draft_name state_type="iana-action" %}"{% endif %}>&nbsp;</a>{% endif %}
{% if info.rfc_editor_state %}<br />RFC Editor State: <a href="http://www.rfc-editor.org/queue2.html#{{ doc.draft_name }}">{{ info.rfc_editor_state|escape }}</a>{% endif %}
{% ifequal doc.draft_status "Expired" %}
{% if doc.resurrect_requested_by %}(resurrect requested by {{ doc.resurrect_requested_by }}){% endif %}
{% endifequal %}
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
{% if add_link %}<a class="editlink" href="{% url doc_change_telechat_date name=doc.draft_name %}">{% endif %}
{% if doc.underlying_document.telechat_date %}<br/>On agenda of {{ doc.underlying_document.telechat_date }} IESG telechat {% if doc.underlying_document.returning_item %} (returning item){% endif %}{% else %}{%if add_link %}<br/>Not on an upcoming telechat agenda{% endif %}{% endif %}
{% if add_link %}</a>{% endif %}
{% if doc.ietf_process.has_active_iesg_ballot %}<br/><i>({{ doc.ietf_process.iesg_ballot_needed }})</i>{%endif%}
{% endwith %}
</td></tr>
{# <tr><td>Submission:</td><td>{{ doc.submission|safe }}</td></tr> #}
<tr><td>Responsible AD:</td><td>
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
{% if add_link %}<a class="editlink" href="{% url doc_change_ad name=doc.draft_name %}">{% endif %}
{% if doc.in_ietf_process %}{{ doc.ietf_process.ad_name|default:"(None)"|escape }}{%else%}(None){%endif%}
{% if add_link %}</a>{% endif %}
{% endwith %}
</td></tr>
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
{% if doc.in_ietf_process %}{% if doc.ietf_process.iesg_note or add_link %}<tr><td>
IESG Note:</td><td>
{% if add_link %}<a class="editlink" href="{% url doc_change_iesg_note name=doc.draft_name %}">{% endif %}
{{ doc.ietf_process.iesg_note|format_textarea|safe }}
{% if add_link %}</a>{% endif %}
</td></tr>{% endif %}{% endif %}
{% endwith %}
<tr><td>Send notices to:</td><td>
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
{% if add_link %}<a class="editlink" href="{% url doc_change_notify name=doc.draft_name %}">{% endif %}
{{ doc.underlying_document.notify|default:"No addresses provided"}}
{% if add_link %}</a>{% endif %}
{% endwith %}
</td></tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
{% endblock doc_metatable %}
{% block doc_metalinks %}
{% edit_actions doc %}
<div>
<a href="mailto:{{doc.draft_name}}@tools.ietf.org?subject=Mail%20regarding%20{{doc.draft_name}}" rel="nofollow">Email Authors</a>
| <a href="/ipr/search/?option=document_search&amp;id_document_tag={{doc.tracker_id}}" rel="nofollow">IPR Disclosures</a>
| <a href="http://www.fenron.net/~fenner/ietf/deps/index.cgi?dep={{doc.draft_name}}" rel="nofollow">Dependencies to this draft</a>
| <a href="http://tools.ietf.org/idnits?url=http://tools.ietf.org/id/{{doc.draft_name_and_revision}}.txt" rel="nofollow" target="_blank">Check nits</a>
{% if doc.in_ietf_process %}| <a href="/feed/comments/{% if info.is_rfc %}rfc{{doc.rfc_number}}{% else %}{{doc.draft_name}}{% endif %}/">Comments feed</a>{% endif %}
| <a href="http://www.google.com/search?as_q={{doc.draft_name}}&as_sitesearch={{ doc.search_archive }}" rel="nofollow" target="_blank">Search Mailing Lists</a>
{% if user|in_group:"Area_Director" %}
| <a href="https://www.iesg.org/bin/c5i?mid=6&rid=77&target={{doc.draft_name}}" rel="nofollow" target="_blank">Search Mailing Lists (ARO)</a>
{% endif %}
</div>
{% if user|in_group:"Area_Director,Secretariat" %}
<div>
{% if doc.in_ietf_process %}
<a href="{% url doc_ballot_lastcall name=doc.draft_name %}">Last Call Text</a>
| <a href="{% url doc_ballot_writeupnotes name=doc.draft_name %}">Ballot Text</a>
| <a href="{% url doc_ballot_approvaltext name=doc.draft_name %}">Announcement Text</a>
{% endif %}
</div>
{% endif %}
{% endblock %}
{% block doc_text1 %}
{% ifequal doc.draft_status "Active" %}
<div class="markup_draft">
{{ content1|safe }}
</div>
{% else %}
<p>This Internet-Draft is no longer active. Unofficial copies of old Internet-Drafts can be found here:<br/>
<a href="http://tools.ietf.org/id/{{doc.draft_name}}">http://tools.ietf.org/id/{{doc.draft_name}}</a>.</p>
<p style="max-width: 400px;"><b>Abstract:</b><br/> {{ doc.abstract|escape }}</p>
<p><b>Authors:</b><br/>
{% for author in doc.authors.all %}
{% if author.email %}
<a href="mailto:{{ author.email }}">{{ author.person }} &lt;{{author.email}}&gt;</a>
{% else %}
{% if author.person %}
{{ author.person }}
{% else %}
Missing author info #{{ author.person_id }}
{% endif %}
{% endif %}<br />
{% endfor %}</p>
<p>(Note: The e-mail addresses provided for the authors of this Internet-Draft may no longer be valid)</p>
{% endifequal %}
{% endblock %}{# doc_text1 #}
{% block doc_text2 %}
{% ifequal doc.draft_status "Active" %}
<div class="markup_draft">
{{ content2|safe }}
</div>
{% endifequal %}
{% endblock %} {# doc_text2 #}

View file

@ -1,112 +0,0 @@
{% extends "idrfc/doc_tab_document.html" %}
{% 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 %}
{% block doc_metatable %}
<tr><td style="width:18ex;">Document type:</td><td>RFC - {{ doc.maturity_level }} {% if doc.stream_name %}({{ doc.stream_name }}){% endif %}
{% if doc.obsoleted_by %}<br />Obsoleted by {{ doc.obsoleted_by|urlize_ietf_docs }}{%endif %}
{% if doc.updated_by %}<br />Updated by {{ doc.updated_by|urlize_ietf_docs }}{%endif %}
{% if doc.obsoletes %}<br />Obsoletes {{ doc.obsoletes|urlize_ietf_docs }}{%endif %}
{% if doc.updates %}<br />Updates {{ doc.updates|urlize_ietf_docs }}{%endif %}
{% if info.status_changes %}<br />Status changed by {{ info.status_changes|urlize_ietf_docs }}{% endif %}
{% if info.proposed_status_changes %}<br />Proposed status changes by {{ info.proposed_status_changes|urlize_ietf_docs }}{% endif %}
{% if doc.also %}<br />Also Known As {{ doc.also|urlize_ietf_docs }}{%endif %}
{% if doc.draft_name %}<br />Was <a href="/doc/{{ doc.draft_name}}/">{{doc.draft_name}}</a>{% endif %}
{% if doc.has_errata %}<br /><a href="http://www.rfc-editor.org/errata_search.php?rfc={{doc.rfc_number}}" rel="nofollow">Errata</a>{% endif %}
</td></tr>
<tr><td>Published:</td><td> {{ doc.publication_date|date:"Y-m"|default:"(data missing)" }}</td></tr>
{% if doc.in_ietf_process %}
<tr class="post-rfc"><td colspan="2">* This information refers to IESG processing after the RFC was initially published</td></tr>
<tr class="post-rfc"><td><a href="/idtracker/help/state/">State</a>:</td><td>
{{ doc.friendly_state|safe }} (IESG: {{ doc.ietf_process.state}})
{% if doc.ietf_process.telechat_date %}<br/>On agenda of {{ doc.ietf_process.telechat_date }} IESG telechat {% if doc.ietf_process.telechat_returning_item %} (returning item){%endif%}{%endif%}
</td></tr>
<tr class="post-rfc"><td>Intended status:</td><td>{% if doc.in_ietf_process %}{{ doc.ietf_process.intended_maturity_level|default:"-" }}{% else %}-{%endif%}</td></tr>
<tr class="post-rfc"><td>Responsible AD:</td><td>{{ doc.ietf_process.ad_name|default:"-"|escape }}</td></tr>
{% if doc.ietf_process.iesg_note %}<tr class="post-rfc"><td>IESG Note:</td><td>{{ doc.ietf_process.iesg_note|escape }}</td></tr>{% endif %}
{% if user|in_group:"Area_Director,Secretariat" %}
<tr class="post-rfc"><td>Send notices to:</td><td>{{ doc.ietf_process.state_change_notice_to}}</td></tr>
{% endif %}{# if user|in_group:... #}
{% endif %}
<tr><td>Other versions:</td><td>
{% if info.has_txt or info.has_ps or info.has_pdf %}
{% if info.has_txt %}
<a href="http://www.rfc-editor.org/rfc/rfc{{doc.rfc_number}}.txt">plain text</a>,
{% endif %}
{% if info.has_ps %}
<a href="http://www.rfc-editor.org/rfc/rfc{{doc.rfc_number}}.ps">ps</a>,
{% endif %}
{% if info.has_pdf %}
<a href="http://www.rfc-editor.org/rfc/rfc{{doc.rfc_number}}.pdf">pdf</a>,
{% else %}
{% if info.has_txt %}
<a href="http://www.rfc-editor.org/rfc/pdfrfc/rfc{{doc.rfc_number}}.txt.pdf">pdf</a>,
{% endif %}
{% endif %}
{% if info.has_txt %}
<a href="http://tools.ietf.org/html/rfc{{doc.rfc_number}}">html</a>
{% endif %}
{% else %}
(not online)
{% endif %}
</td></tr>
{% endblock doc_metatable %}
{% block doc_metalinks %}
<a href="/ipr/search/?option=rfc_search&amp;rfc_search={{doc.rfc_number}}" rel="nofollow">IPR Disclosures</a>
| <a href="http://www.fenron.net/~fenner/ietf/deps/index.cgi?dep=rfc{{doc.rfc_number}}" rel="nofollow">Dependencies to this RFC</a>
{% endblock %}
{% block doc_text1 %}
{% if info.has_txt %}
<div class="markup_draft">
{{ content1|safe }}
</div>
{% else %}
{% if info.has_pdf or info.has_ps %}
<p>This RFC is not available in plain text format.</p>
{% else %}
<p>This RFC is not currently available online.</p>
{% endif %}
{% endif %}
{% endblock %}{# doc_text1 #}
{% block doc_text2 %}
<div class="markup_draft">
{{ content2|safe }}
</div>
{% endblock %}{# doc_text2 #}

View file

@ -1,52 +0,0 @@
{% extends "idrfc/doc_main.html" %}
<!--
{% 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 %}
{% block tab_content %}
{% if not info.is_rfc %}
{% include "idrfc/doc_diffs.html" %}
{% endif %}
<h2 style="margin-top:1em;">Document history</h2>
{% if user|in_group:"Area_Director,Secretariat,IANA,RFC Editor" and doc.in_ietf_process %}
<div style="margin-bottom:8px" id="history_actions">
<span id="doc_add_comment_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url doc_add_comment name=doc.draft_name %}">Add comment</a></span></span>
</div>
{% endif %}
{% include "idrfc/doc_history.html" %}
{% endblock tab_content %}

View file

@ -1,73 +0,0 @@
{% extends "idrfc/doc_main.html" %}
{% 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 %}
{% block tab_content %}
---- following is a DRAFT of message to be sent AFTER approval ---
{% if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot %}
{% if user|in_group:"Area_Director,Secretariat" %}
<div style="background:#E0E0FF">
<p align=right>
<span id="doc_edit_announce_button" class="yui-button yui-link-button"><span class="first-child">
<a href="{% url doc_ballot_approvaltext name=doc.draft_name %}">Edit Announcement Text</a>
</span></span>
</p>
{% endif %}
{% endif %}
<pre>
{{ doc.ietf_process.iesg_ballot.approval_text|escape|urlize }}
</pre>
{% if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot %}
{% if user|in_group:"Area_Director,Secretariat" %}
</div>
<div style="background:#E0E0FF">
<p align=right>
<span id="doc_ballot_edit_button" class="yui-button yui-link-button"><span class="first-child">
<a href="{% url doc_ballot_writeupnotes name=doc.draft_name %}">Edit Ballot Text</a>
</span></span>
</p>
{% endif %}
{% endif %}
<pre>
{{ doc.ietf_process.iesg_ballot.ballot_writeup|escape|urlize }}
</pre>
{% if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot %}
{% if user|in_group:"Area_Director,Secretariat" %}
</div>
{% endif %}
{% endif %}
{% endblock tab_content %}

View file

@ -1,5 +0,0 @@
{% if info.is_rfc %}
RFC {{ doc.rfc_number }}
{% else %}
{{ doc.draft_name_and_revision }}
{% endif %}

View file

@ -77,11 +77,6 @@ about:
{% endif %}
</div>
{% endblock content %}
{% block scripts %}
YAHOO.util.Event.onContentReady("search_submit_button", function () {
var oButton = new YAHOO.widget.Button("search_submit_button", {});
});
{% endblock scripts %}
{% block js %}
<script type="text/javascript" src="/js/doc-search.js"></script>

View file

@ -32,54 +32,46 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}
<form name="search_form" id="search_form" class="search_form" action="/doc/search/" method="get" {# onsubmit="submitSearch();return false;" #}>
<form id="search_form" class="search_form" action="/doc/search/" method="get">
<div class="search_field">
<label>Name/number/title:</label> {{ form.name }}
</div>
<div class="search_field">
<label>Types:</label>
<table id="search_types">
<tr><td>{{ form.rfcs }} RFCs</td></tr>
<tr><td>{{ form.activeDrafts }} Internet-Drafts (active)</td></tr>
<tr><td>{{ form.oldDrafts }} Internet-Drafts (expired/replaced/withdrawn)</td></tr>
</table>
<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>
</table>
</div>
<span class="toggle_advanced"><b><img src="/images/{% if meta.advanced %}minus{% else %}plus{% endif %}.png" alt="" id="search_advanced-img" /> Advanced</b></span>
<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%}margin-top:1em;">
<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" {% ifequal meta.by "author" %}checked="checked"{% endifequal %}/> Author (last name):</label> {{ form.author }}
<label><input type="radio" class="radio" name="by" value="author" {% if meta.by == "author" %}checked="checked"{% endif %}/> Author (last name):</label> {{ form.author }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="group" {% ifequal meta.by "group" %}checked="checked"{% endifequal %}/> WG:</label> {{ form.group }}
<label><input type="radio" class="radio" name="by" value="group" {% if meta.by == "group" %}checked="checked"{% endif %}/> WG:</label> {{ form.group }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="area" {% ifequal meta.by "area" %}checked="checked"{% endifequal %}/> Area:</label> {{ form.area }}
<label><input type="radio" class="radio" name="by" value="area" {% if meta.by == "area" %}checked="checked"{% endif %}/> Area:</label> {{ form.area }}
</div>
<div class="search_field">
<label><input type="radio" class="radio" name="by" value="ad" {% ifequal meta.by "ad" %}checked="checked"{% endifequal %}/> Responsible AD:</label> {{ form.ad }}
<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" {% ifequal meta.by "state" %}checked="checked"{% endifequal %}/> 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>
{% comment %}
<div class="search_field" style="text-decoration:line-through;color:#808080;">
<label>Ballot position:</label> {{ form.positionAd }} has position {{ form.positionValue }}
</div>
{% endcomment %}
</div><!-- end of advanced -->
<div style="padding-top:0.5em;">
{# <input type="hidden" name="ajax" value="" /> #}
<span class="first-child">
<button type="submit" name="search_submit" id="id_search_submit">Search</button>
</span>
<div class="submit">
<input type="submit" class="button" value="Search"/>
</div>
</form>

View file

@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% block content %}
<h1>Internet-Drafts and RFCs</h1>
<div class="ietf-box search_form_box">
<div class="ietf-box search-form-box">
{% include "idrfc/search_form.html" %}
</div>
@ -48,32 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endif %}
</div>
{% endblock content %}
{% block scripts %}
YAHOO.util.Event.onContentReady("search_submit_button", function () {
var oButton = new YAHOO.widget.Button("search_submit_button", {});
});
{% if meta.searching %}
(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);
{% endif %}
{% endblock scripts %}
{% block js %}
<script type="text/javascript" src="/js/doc-search.js"></script>

View file

@ -35,7 +35,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% load ietf_filters %}
{% load ballot_icon %}
<tr class="{{ forloop.counter|divisibleby:2|yesno:"oddrow,evenrow" }}">
{% if user.is_authenticated and show_add_to_list %}
{% 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>
@ -51,7 +51,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endif %}
</td>
<td class="title">{{ doc.title }}</td>
{% include "idrfc/date_column.html" %}
<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>
{% include "idrfc/status_columns.html" %}
{% include "idrfc/ipr_column.html" %}
{# <td class="ad">{% if doc.ad_name %}{{ doc.ad_name }}{% else %}&nbsp;{% endif %}</td> #}

View file

@ -38,19 +38,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% if not docs %}
<p><b>No documents match your query.</b></p>
{% else %}
{% regroup docs by view_sort_group as grouped_docs %}
<table class="ietf-table ietf-doctable">
<tr>{% if user.is_authenticated %}<th></th>{% endif %}
{% for hdr in meta.hdrs %}
{% include "idrfc/table_header.html" %}
{% endfor %}
<tr>
{% if 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>
</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> -->
{% for doc_group in grouped_docs %}
<tr class="header"><td colspan="7">{{doc_group.grouper}}s</td></tr>
{% regroup docs by view_sort_group 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" %}
{% include "idrfc/search_result_row.html" %}
{% endfor %}
{% endwith %}

View file

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

View file

@ -20,6 +20,6 @@
{% 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|safe|urlize }}</td></tr>
<tr><td></td><td>Note:</td><td>{{ doc.note|linebreaksbr|urlize }}</td></tr>
{% endif %}
{% endif %}

View file

@ -83,7 +83,7 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
<a href="http://www.ietf.org/id/{{doc.obj.conflictdoc.name}}-{{doc.obj.conflictdoc.rev}}.txt">[txt]</a>
<br/>{{ doc.obj.conflictdoc.title|escape }} ({{doc.obj.conflictdoc.stream}}: {{ doc.obj.conflictdoc.intended_std_level }})
{% if doc.obj.conflictdoc.note %}
<br/>Note: {{ doc.obj.conflictdoc.note|unescape }}
<br/>Note: {{ doc.obj.conflictdoc.note|linebreaksbr }}
{% endif %}
{% if doc.obj.conflictdoc.ipr %}

View file

@ -85,7 +85,7 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{% if doc.obj.note %}
<br/>Note: {{ doc.obj.note|unescape }}
<br/>Note: {{ doc.obj.note|linebreaksbr }}
{% endif %}
{% if doc.obj.ipr %}

View file

@ -51,8 +51,8 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{{ doc.obj.conflictdoc.title|escape }} ({{doc.obj.conflictdoc.stream}}: {{ doc.obj.conflictdoc.intended_std_level }})
<br><a href="{% url doc_view name=doc.obj.conflictdoc.canonical_name %}">{{doc.obj.conflictdoc.canonical_name}}</a>
<a href="http://www.ietf.org/id/{{doc.obj.conflictdoc.canonical_name}}-{{doc.obj.conflictdoc.rev}}.txt">[txt]</a>
{% if doc.obj.conflictdoc.note %}{# note: note is not escaped #}
<br>Note: {{ doc.obj.conflictdoc.note|safe }}
{% if doc.obj.conflictdoc.note %}
<br>Note: {{ doc.obj.conflictdoc.note|linebreaksbr }}
{% endif %}
{% for ipr in doc.obj.conflictdoc.ipr %}
{% ifequal ipr.ipr.status 1 %}

View file

@ -55,7 +55,7 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{% endwith %}
<br>Token: {{ doc.obj.ad.plain_name|escape }} ({{doc.obj.area_acronym}} area)
{% if doc.obj.note %}
<br>Note: {{ doc.obj.note }}
<br>Note: {{ doc.obj.note|linebreaksbr }}
{% endif %}
{% for ipr in doc.obj.ipr %}
{% ifequal ipr.ipr.status 1 %}

View file

@ -10,7 +10,7 @@ label { float:left; width: 200px; }
<h1>IPR Search</h1>
<h2>Document Search</h2>
<div class="ietf-box search_form_box">
<div class="ietf-box search-form-box">
<form method="get">
<input type="hidden" name="option" value="document_search">

View file

@ -77,7 +77,7 @@ is occasionally incorrect.</span>
{% else %}
none
{% if user|has_role:"Area Director,Secretariat" %}
- <a href="{% url wginfo.edit.submit_initial_charter acronym=wg.acronym %}">Submit Charter</a>
- <a href="{% url ietf.wginfo.edit.submit_initial_charter acronym=wg.acronym %}">Submit Charter</a>
{% endif %}
{% endif %}
</td>

View file

@ -411,17 +411,16 @@ def make_test_data():
# an independent submission before review
doc = Document.objects.create(name='draft-imaginary-independent-submission',type_id='draft')
doc.set_state(State.objects.get(used=True, type="draft", slug="active"))
DocAlias.objects.create( name='draft-imaginary-independent-submission',document=doc)
DocAlias.objects.create(name=doc.name, document=doc)
# an irtf submission mid review
doc = Document.objects.create( name='draft-imaginary-irtf-submission',type_id='draft')
docalias = DocAlias.objects.create(name='draft-imaginary-irtf-submission',document=doc)
doc = Document.objects.create(name='draft-imaginary-irtf-submission', type_id='draft')
docalias = DocAlias.objects.create(name=doc.name, document=doc)
doc.stream = StreamName.objects.get(slug='irtf')
doc.save()
crdoc = Document.objects.create(name='conflict-review-imaginary-irtf-submission',type_id='conflrev',rev='00',notify="fsm@ietf.org")
DocAlias.objects.create( name='conflict-review-imaginary-irtf-submission',document=crdoc)
crdoc.set_state(State.objects.get(used=True, name='Needs Shepherd',type__slug='conflrev'))
crdoc.save()
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'))
crdoc.relateddocument_set.create(target=docalias,relationship_id='conflrev')
# A status change mid review

View file

@ -4,7 +4,6 @@ from django.shortcuts import get_object_or_404, render_to_response
from django.template import RequestContext
from django.http import HttpResponseForbidden, Http404
from ietf.idrfc.views_search import SearchForm, search_query
from ietf.wgchairs.forms import (RemoveDelegateForm, add_form_factory,
workflow_form_factory, TransitionFormSet,
WriteUpEditForm, assign_shepherd)
@ -186,15 +185,7 @@ def wg_shepherd_documents(request, acronym):
return HttpResponseForbidden('You have no permission to access this view')
current_person = get_person_for_user(user)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
base_qs = InternetDraft.objects.filter(group=wg, states__type="draft", states__slug="active").select_related("status").order_by('title')
else:
form = SearchForm({'by': 'group', 'group': str(wg.group_acronym.acronym),
'activeDrafts': 'on'})
if not form.is_valid():
raise ValueError("form did not validate")
(docs, meta) = search_query(form.cleaned_data)
base_qs = InternetDraft.objects.filter(pk__in=[i.id._draft.pk for i in docs if i.id]).select_related('status')
base_qs = InternetDraft.objects.filter(group=wg, states__type="draft", states__slug="active").select_related("status").order_by('title')
documents_no_shepherd = base_qs.filter(shepherd=None)
documents_my = base_qs.filter(shepherd=current_person)
documents_other = base_qs.exclude(shepherd=None).exclude(shepherd__pk__in=[current_person.pk, 0])

View file

@ -82,13 +82,16 @@ body { margin: 0; }
.ietf-stream .entry-title .entry-date { float: right; }
.ietf-stream .entry-comment { background: #eeeeee; margin: 1em 0px; padding: 1em; }
.search_form_box {width: 99.5%; margin-top:8px; padding:4px; margin-bottom:1em; padding-left:8px;}
.search-form-box { width: 99.5%; margin-top:8px; padding:4px; margin-bottom:1em; padding-left:8px; }
form#search_form { padding-top: 4px; padding-bottom: 4px; }
#search_form .submit { padding-top: 0.5em; }
#search_form .submit .button { padding: 0.2em 0.5em; border: 1px solid #999; }
#search_form input { padding: 0; padding-left: 2px; border: 1px solid #89d;}
#search_form input.radio { padding-left: 0; border: 0; }
#search_form input.radio { padding-left: 0; border: 0; }
#search_form select { border: 1px solid #89d; }
#search_form div.search_field { margin-top:2px; clear:both;}
#search_form label { width: 170px; float: left; }
#search_form .search_field > label { width: 170px; float: left; }
#search_form #search_advanced { margin-top: 1em; }
/* checkboxes for document types */
#search_form table#search_types { border-collapse:collapse;}
#search_form #search_types td { padding:0; }
@ -106,12 +109,13 @@ table.ietf-table { border-collapse:collapse; border:1px solid #7f7f7f; }
.ietf-table tr.evenrow { background-color: #EDF5FF; }
.ietf-table tr.oddrow { background-color: white; }
.ietf-table td { border-right: 1px solid #cbcbcb; padding:3px 6px; vertical-align: top; }
.ietf-table th { color:white; background: #2647A0; text-align:left; padding:3px 6px; border-right: 1px solid #7f7f7f; }
.ietf-table th { color: #fff; background: #2647A0; text-align: left; padding:3px 6px; border-right: 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 }
.ietf-doctable th { cursor: pointer; white-space: nowrap; }
.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; }
.ietf-doctable th.date, .ietf-doctable td.date { white-space:nowrap; min-width: 6em;}
@ -209,7 +213,13 @@ 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 { display: inline-block; font-weight: normal; background: #eee; border: 1px solid #bbb; border-radius: 3px; color: #333; padding: 2px 8px; text-align: center; text-decoration: none; outline: none; transition-duration: 0.2s; cursor: pointer }
.button:hover { background: #ddd; color: #222; }
.button:active { background: #ccc; color: #000; }
.button, .button:hover:disabled {
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);
border: 1px solid #666; border-radius: 3px;
transition-duration: 0.2s;
}
.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; }

View file

@ -3,26 +3,46 @@
#metatable tr { vertical-align: top; }
#metatable tr:first-child td:first-child { width: 15em; }
.markup_draft pre { line-height: 1.2em; margin: 0; }
.m_hdr, .m_ftr { color: #808080; }
.m_ftr { border-bottom: 1px solid #a0a0a0; }
.m_h { font-family: arial; font-weight:bold;}
.stream-tags { font-style: italic; }
.document-markup pre { line-height: 1.2em; margin: 0; }
.document-markup .m_hdr, .m_ftr { color: #808080; }
.document-markup .m_ftr { border-bottom: 1px solid #a0a0a0; }
.document-markup .m_h { font-family: arial; font-weight:bold;}
.snapshots { margin: 0.5em 0; }
.snapshots .revisions a:last-child { font-weight: bold; }
.metabox .actions a { display: inline-block; margin-right: 0.4em; }
.metabox .links { margin-bottom: 0.2em; }
.metabox .actions { margin-top: 0.5em; }
.metabox .ballot-summary { font-style: italic; }
.metabox .telechat { margin-top: 0.2em; }
.diffTool { padding: 8px 4px; margin: 8px 0;}
.diffTool h2 { margin-top:0;margin-bottom:4px; }
.diffTool label { display: inline-block; width: 3em; padding: 0 0.5em; }
.diffTool form { margin: 0; }
.diff-tool { padding: 8px 4px; margin: 8px 0;}
.diff-tool h2 { margin-top:0;margin-bottom:4px; }
.diff-tool label { display: inline-block; width: 3em; padding: 0 0.5em; }
.diff-tool form { margin: 0; }
.history-actions { margin-bottom: 1em; padding-left: 1px; }
.writeup pre.editable { background-color: #efefff; min-height: 3em; }
.writeup a.edit { float: right; }
.writeup pre.editable { background-color: #efefff; min-height: 3em; padding: 4px; }
.writeup a.edit { float: right; margin: 4px; }
a.editlink {
background-image: url("/images/pencil.png");
background-size: 10px;
background-position: right top;
background-attachment: scroll;
background-repeat: no-repeat;
padding-right: 12px;
}
a.editlink:link {text-decoration:none; color:inherit;}
a.editlink:visited {text-decoration:none; color:inherit;}
a.editlink:hover {text-decoration:underline;}
a.editlink:active {text-decoration:underline;}
h3 a.edit { font-weight: normal; font-size: 13px; display: inline-block; margin-left: 0.5em;}
h4 { margin-bottom: 0; }
h4 + p { margin-top: 0; max-width: 400px; }

View file

@ -1,86 +1,77 @@
$(function () {
var form = jQuery("#search_form");
form.find(".search_field input[name=by]").parents("label").click(changeBy);
form.find(".search_field").find("input,select")
.change(toggleSubmit).click(toggleSubmit).keyup(toggleSubmit);
form.find(".toggle_advanced").click(function () {
togglePlusMinus("search_advanced");
form.find('.search_field input[type="radio"]').attr("checked", false);
changeBy();
});
changeBy();
// we want to disable our submit button if we have no search text,
// we want to disable our submit button if we have no search text,
// and we have no advanced options selected
function toggleSubmit() {
var button = document.getElementById("id_search_submit");
var by = findCheckedSearchBy();
var value = findSearchByValue(by);
var text = document.getElementById("id_name");
if ((value == "") && (text.value == "")) {
button.disabled = true;
} else {
button.disabled = false;
}
var nameSearch = $.trim($("#id_name").val());
var noAdvanced = true;
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;
});
form.find("input[type=submit]").get(0).disabled = !nameSearch && noAdvanced;
}
function togglePlusMinus(id) {
var el = document.getElementById(id);
var imgEl = document.getElementById(id+"-img");
if (el.style.display == 'none') {
el.style.display = 'block';
imgEl.src = "/images/minus.png";
function togglePlusMinus(toggler, toggled) {
var img = toggler.find("img").get(0);
if (toggled.is(":hidden")) {
toggled.show();
img.src = "/images/minus.png";
} else {
el.style.display = 'none';
imgEl.src = "/images/plus.png";
toggled.hide();
img.src = "/images/plus.png";
}
}
function findCheckedSearchBy() {
var by='';
var f = document.search_form;
for (var i = 0; i < f.by.length; i++) {
if (f.by[i].checked) {
by = f.by[i].value;
break;
}
}
return by;
}
function updateBy() {
form.find("input[name=by]:checked").closest(".search_field").find("input,select").not("input[name=by]").each(function () {
this.disabled = false;
});
function findSearchByValue(by) {
if (by == 'author') { return document.getElementById("id_author").value; }
if (by == 'group') { return document.getElementById("id_group").value; }
if (by == 'area') { return document.getElementById("id_area").value; }
if (by == 'ad') { return document.getElementById("id_ad").value; }
if (by == 'state') {
// state might be state...
state_value = document.getElementById("id_state").value;
if (state_value) { return state_value; }
// ...or sub-state
return document.getElementById("id_subState").value;
}
return '';
}
function changeBy() {
var by = findCheckedSearchBy();
var f = document.search_form;
f.author.disabled=true;
f.group.disabled=true;
f.area.disabled=true;
f.ad.disabled=true;
f.state.disabled=true; f.subState.disabled=true;
if (by=='author') { f.author.disabled=false;}
if (by=='group') { f.group.disabled=false;}
if (by=='area') { f.area.disabled=false;}
if (by=='ad') { f.ad.disabled=false; }
if (by=='state') { f.state.disabled=false; f.subState.disabled=false; }
form.find("input[name=by]").not(":checked").closest(".search_field").find("input,select").not("input[name=by]").each(function () {
this.disabled = true;
});
toggleSubmit();
}
form.find(".search_field input[name=by]").closest("label").click(updateBy);
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();
});
updateBy();
$("#search_results th").click(function (e) {
window.location = $(this).find("a").attr("href");
})
$('#search_results .addtolist a').click(function(e) {
e.preventDefault();
var trigger = $(this);
$.ajax({
url: trigger.attr('href'),
type: 'POST',
cache: false,
dataType: 'json',
success: function(response){
if (response.success) {
trigger.replaceWith('added');
}
}
});
});
});