304 lines
13 KiB
Python
304 lines
13 KiB
Python
# 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
|
|
from datetime import datetime, time
|
|
|
|
from django.http import HttpResponse, Http404
|
|
from django.shortcuts import render_to_response, get_object_or_404
|
|
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.models import RfcIndex, DraftVersions
|
|
from ietf.idrfc.idrfc_wrapper import BallotWrapper, IdWrapper, RfcWrapper
|
|
from ietf.ietfworkflows.utils import get_full_info_for_draft
|
|
|
|
def document_debug(request, name):
|
|
r = re.compile("^rfc([1-9][0-9]*)$")
|
|
m = r.match(name)
|
|
if m:
|
|
rfc_number = int(m.group(1))
|
|
rfci = get_object_or_404(RfcIndex, rfc_number=rfc_number)
|
|
doc = RfcWrapper(rfci)
|
|
else:
|
|
id = get_object_or_404(InternetDraft, filename=name)
|
|
doc = IdWrapper(draft=id)
|
|
return HttpResponse(doc.to_json(), mimetype='text/plain')
|
|
|
|
def _get_html(key, filename):
|
|
f = None
|
|
try:
|
|
f = open(filename, 'rb')
|
|
raw_content = f.read()
|
|
except IOError:
|
|
return ("Error; cannot read ("+key+")", "")
|
|
finally:
|
|
if f:
|
|
f.close()
|
|
(c1,c2) = markup_txt.markup(raw_content)
|
|
return (c1,c2)
|
|
|
|
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)
|
|
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 = ""
|
|
|
|
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(request, name, tab):
|
|
if tab is None:
|
|
tab = "document"
|
|
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
|
|
|
|
(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),
|
|
'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://tools.ietf.org/rfcdiff?url2=%s">diff from -%02d</a>)' % (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 = 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(format_textarea(fill(info['text'], 80)), 25)
|
|
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):
|
|
if x['date']:
|
|
x['date'] = datetime.combine(x['date'], time(0,0,0))
|
|
else:
|
|
x['date'] = datetime(1970,1,1)
|
|
|
|
results.sort(key=lambda x: x['date'])
|
|
results.reverse()
|
|
return results
|
|
|
|
# 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):
|
|
r = re.compile("^rfc([1-9][0-9]*)$")
|
|
m = r.match(name)
|
|
|
|
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
|
from ietf.doc.models import DocAlias
|
|
alias = get_object_or_404(DocAlias, name=name)
|
|
d = get_object_or_404(InternetDraft, name=alias.document.name)
|
|
try:
|
|
if not d.ballot.ballot_issued:
|
|
raise Http404
|
|
except BallotInfo.DoesNotExist:
|
|
raise Http404
|
|
|
|
return (BallotWrapper(d), RfcWrapper(d) if m else IdWrapper(d))
|
|
|
|
if m:
|
|
rfc_number = int(m.group(1))
|
|
rfci = get_object_or_404(RfcIndex, rfc_number=rfc_number)
|
|
id = get_object_or_404(IDInternal, rfc_flag=1, draft=rfc_number)
|
|
doc = RfcWrapper(rfci, idinternal=id)
|
|
else:
|
|
id = get_object_or_404(IDInternal, rfc_flag=0, draft__filename=name)
|
|
doc = IdWrapper(id)
|
|
try:
|
|
if not id.ballot.ballot_issued:
|
|
raise Http404
|
|
except BallotInfo.DoesNotExist:
|
|
raise Http404
|
|
|
|
ballot = BallotWrapper(id)
|
|
return ballot, doc
|
|
|
|
def document_ballot(request, name):
|
|
ballot, doc = get_ballot(name)
|
|
return render_to_response('idrfc/doc_ballot.html', {'ballot':ballot, 'doc':doc}, context_instance=RequestContext(request))
|
|
|
|
def ballot_tsv(request, name):
|
|
ballot, doc = 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 = get_ballot(name)
|
|
response = HttpResponse(mimetype='text/plain')
|
|
response.write(json.dumps(ballot.dict(), indent=2))
|
|
return response
|