Skip to content

Commit 09f654b

Browse files
committed
Merged in branch/iola/team-support from olau@iola.dk. This generalises and extends the group support for WGs and RGs so that also other groups, such as for instance the Edu Team, can have pages.
- Legacy-Id: 8013
2 parents 8e9545e + 20fb8ff commit 09f654b

52 files changed

Lines changed: 1656 additions & 360 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ietf/community/display.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def get_value(self, document, raw=False):
8888
if raw or not document.group.type_id in ['wg','rg']:
8989
return document.group.acronym
9090
else:
91-
return '<a href="%s">%s</a>' % (urlreverse('group_docs', kwargs=dict(group_type=document.group.type_id, acronym=document.group.acronym)), document.group.acronym) if (document.group and document.group.acronym != 'none') else ''
91+
return '<a href="%s">%s</a>' % (urlreverse('group_home', kwargs=dict(group_type=document.group.type_id, acronym=document.group.acronym)), document.group.acronym) if (document.group and document.group.acronym != 'none') else ''
9292

9393

9494
class ADField(DisplayField):

ietf/doc/models.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,10 @@ def file_extension(self):
7474
return ext.lstrip(".").lower()
7575

7676
def get_file_path(self):
77+
7778
if self.type_id == "draft":
7879
return settings.INTERNET_DRAFT_PATH
79-
elif self.type_id in ("agenda", "minutes", "slides"):
80+
elif self.type_id in ("agenda", "minutes", "slides") and self.meeting_related():
8081
meeting = self.name.split("-")[1]
8182
return os.path.join(settings.AGENDA_PATH, meeting, self.type_id) + "/"
8283
elif self.type_id == "charter":
@@ -86,18 +87,26 @@ def get_file_path(self):
8687
elif self.type_id == "statchg":
8788
return settings.STATUS_CHANGE_PATH
8889
else:
89-
raise NotImplemented
90+
return settings.DOCUMENT_PATH_PATTERN.format(doc=self)
9091

9192
def href(self):
93+
meeting_related = self.meeting_related()
94+
95+
settings_var = settings.DOC_HREFS
96+
if meeting_related:
97+
settings_var = settings.MEETING_DOC_HREFS
98+
9299
try:
93-
format = settings.DOC_HREFS[self.type_id]
100+
format = settings_var[self.type_id]
94101
except KeyError:
95102
if len(self.external_url):
96103
return self.external_url
97104
return None
105+
98106
meeting = None
99-
if self.type_id in ("agenda", "minutes", "slides"):
107+
if meeting_related:
100108
meeting = self.name.split("-")[1]
109+
101110
return format.format(doc=self,meeting=meeting)
102111

103112
def set_state(self, state):
@@ -120,6 +129,9 @@ def get_state(self, state_type=None):
120129
"""Get state of type, or default state for document type if
121130
not specified. Uses a local cache to speed multiple state
122131
reads up."""
132+
if self.pk == None: # states is many-to-many so not in database implies no state
133+
return None
134+
123135
if state_type == None:
124136
state_type = self.type_id
125137

@@ -158,6 +170,11 @@ def active_ballot(self):
158170
else:
159171
return None
160172

173+
def meeting_related(self):
174+
return(self.type_id in ("agenda", "minutes", "slides") and (
175+
self.name.split("-")[1] == "interim"
176+
or (self.session_set.exists() if isinstance(self, Document) else self.doc.session_set.exists())))
177+
161178
class Meta:
162179
abstract = True
163180

@@ -270,12 +287,9 @@ def canonical_name(self):
270287
a = self.docalias_set.filter(name__startswith="rfc")
271288
if a:
272289
name = a[0].name
273-
# elif self.type_id == "charter":
274-
# if self.group.type.slug == "rg":
275-
# top_org = "irtf"
276-
# else:
277-
# top_org = "ietf"
278-
# return "charter-%s-%s" % (top_org, self.chartered_group.acronym)
290+
elif self.type_id == "charter":
291+
from ietf.doc.utils_charter import charter_name_for_group
292+
return charter_name_for_group(self.chartered_group)
279293
return name
280294

281295
def canonical_docalias(self):
@@ -468,7 +482,11 @@ class Meta:
468482

469483
class DocHistory(DocumentInfo):
470484
doc = models.ForeignKey(Document, related_name="history_set")
471-
name = models.CharField(max_length=255) # WG charter canonical names can change if the group acronym changes
485+
# the name here is used to capture the canonical name at the time
486+
# - it would perhaps be more elegant to simply call the attribute
487+
# canonical_name and replace the function on Document with a
488+
# property
489+
name = models.CharField(max_length=255)
472490
related = models.ManyToManyField('DocAlias', through=RelatedDocHistory, blank=True)
473491
authors = models.ManyToManyField(Email, through=DocHistoryAuthor, blank=True)
474492
def __unicode__(self):

ietf/doc/templatetags/ietf_filters.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ def expand_comma(value):
2222
long comma-separated lists."""
2323
return value.replace(",", ", ")
2424

25-
@register.filter(name='format_charter')
26-
def format_charter(value):
27-
return value.replace("\n\n", "</p><p>").replace("\n","<br/>\n")
28-
2925
@register.filter
3026
def indent(value, numspaces=2):
3127
replacement = "\n" + " " * int(numspaces)

ietf/doc/tests.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,22 @@ def test_document_conflict_review(self):
217217
r = self.client.get(urlreverse("doc_view", kwargs=dict(name='conflict-review-imaginary-irtf-submission')))
218218
self.assertEqual(r.status_code, 200)
219219

220+
def test_document_material(self):
221+
draft = make_test_data()
222+
223+
doc = Document.objects.create(
224+
name="slides-testteam-test-slides",
225+
rev="00",
226+
title="Test Slides",
227+
group=draft.group,
228+
type_id="slides"
229+
)
230+
doc.set_state(State.objects.get(type="slides", slug="active"))
231+
DocAlias.objects.create(name=doc.name, document=doc)
232+
233+
r = self.client.get(urlreverse("doc_view", kwargs=dict(name=doc.name)))
234+
self.assertEqual(r.status_code, 200)
235+
220236
def test_document_ballot(self):
221237
doc = make_test_data()
222238
ballot = doc.active_ballot()

ietf/doc/tests_material.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Copyright The IETF Trust 2011, All Rights Reserved
2+
3+
import os, shutil
4+
from StringIO import StringIO
5+
from pyquery import PyQuery
6+
7+
from django.conf import settings
8+
from django.core.urlresolvers import reverse as urlreverse
9+
10+
from ietf.doc.models import Document, State, DocAlias
11+
from ietf.group.models import Group
12+
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
13+
14+
class GroupMaterialTests(TestCase):
15+
def setUp(self):
16+
self.materials_dir = os.path.abspath("tmp-document-dir")
17+
os.mkdir(self.materials_dir)
18+
os.mkdir(os.path.join(self.materials_dir, "slides"))
19+
settings.DOCUMENT_PATH_PATTERN = self.materials_dir + "/{doc.type_id}/"
20+
21+
def tearDown(self):
22+
shutil.rmtree(self.materials_dir)
23+
24+
def create_slides(self):
25+
group = Group.objects.create(type_id="team", acronym="testteam", name="Test Team", state_id="active")
26+
27+
doc = Document.objects.create(name="slides-testteam-test-file", rev="00", type_id="slides", group=group)
28+
doc.set_state(State.objects.get(type="slides", slug="active"))
29+
DocAlias.objects.create(name=doc.name, document=doc)
30+
31+
return doc
32+
33+
def test_choose_material_type(self):
34+
group = Group.objects.create(type_id="team", acronym="testteam", name="Test Team", state_id="active")
35+
36+
url = urlreverse('ietf.doc.views_material.choose_material_type', kwargs=dict(acronym=group.acronym))
37+
login_testing_unauthorized(self, "secretary", url)
38+
39+
# normal get
40+
r = self.client.get(url)
41+
self.assertEqual(r.status_code, 200)
42+
self.assertTrue("Slides" in r.content)
43+
44+
def test_upload_slides(self):
45+
group = Group.objects.create(type_id="team", acronym="testteam", name="Test Team", state_id="active")
46+
47+
url = urlreverse('group_new_material', kwargs=dict(acronym=group.acronym, doc_type="slides"))
48+
login_testing_unauthorized(self, "secretary", url)
49+
50+
# normal get
51+
r = self.client.get(url)
52+
self.assertEqual(r.status_code, 200)
53+
54+
content = "%PDF-1.5\n..."
55+
test_file = StringIO(content)
56+
test_file.name = "unnamed.pdf"
57+
58+
# faulty post
59+
r = self.client.post(url, dict(title="", name="", state="", material=test_file))
60+
61+
self.assertEqual(r.status_code, 200)
62+
q = PyQuery(r.content)
63+
self.assertTrue(len(q('form ul.errorlist')) > 0)
64+
65+
test_file.seek(0)
66+
67+
# post
68+
r = self.client.post(url, dict(title="Test File",
69+
name="slides-%s-test-file" % group.acronym,
70+
state=State.objects.get(type="slides", slug="active").pk,
71+
material=test_file))
72+
self.assertEqual(r.status_code, 302)
73+
74+
doc = Document.objects.get(name="slides-%s-test-file" % group.acronym)
75+
self.assertEqual(doc.rev, "00")
76+
self.assertEqual(doc.title, "Test File")
77+
self.assertEqual(doc.get_state_slug(), "active")
78+
79+
with open(os.path.join(self.materials_dir, "slides", doc.name + "-" + doc.rev + ".pdf")) as f:
80+
self.assertEqual(f.read(), content)
81+
82+
# check that posting same name is prevented
83+
test_file.seek(0)
84+
85+
r = self.client.post(url, dict(title="Test File",
86+
name=doc.name,
87+
state=State.objects.get(type="slides", slug="active").pk,
88+
material=test_file))
89+
self.assertEqual(r.status_code, 200)
90+
self.assertTrue(len(q('form ul.errorlist')) > 0)
91+
92+
def test_change_state(self):
93+
doc = self.create_slides()
94+
95+
url = urlreverse('material_edit', kwargs=dict(name=doc.name, action="state"))
96+
login_testing_unauthorized(self, "secretary", url)
97+
98+
# post
99+
r = self.client.post(url, dict(state=State.objects.get(type="slides", slug="deleted").pk))
100+
self.assertEqual(r.status_code, 302)
101+
doc = Document.objects.get(name=doc.name)
102+
self.assertEqual(doc.get_state_slug(), "deleted")
103+
104+
def test_edit_title(self):
105+
doc = self.create_slides()
106+
107+
url = urlreverse('material_edit', kwargs=dict(name=doc.name, action="title"))
108+
login_testing_unauthorized(self, "secretary", url)
109+
110+
# post
111+
r = self.client.post(url, dict(title="New title"))
112+
self.assertEqual(r.status_code, 302)
113+
doc = Document.objects.get(name=doc.name)
114+
self.assertEqual(doc.title, "New title")
115+
116+
def test_revise(self):
117+
doc = self.create_slides()
118+
119+
url = urlreverse('material_edit', kwargs=dict(name=doc.name, action="revise"))
120+
login_testing_unauthorized(self, "secretary", url)
121+
122+
content = "some text"
123+
test_file = StringIO(content)
124+
test_file.name = "unnamed.txt"
125+
126+
# post
127+
r = self.client.post(url, dict(title="New title",
128+
state=State.objects.get(type="slides", slug="active").pk,
129+
material=test_file))
130+
self.assertEqual(r.status_code, 302)
131+
doc = Document.objects.get(name=doc.name)
132+
self.assertEqual(doc.rev, "01")
133+
self.assertEqual(doc.title, "New title")
134+
self.assertEqual(doc.get_state_slug(), "active")
135+
136+
with open(os.path.join(self.materials_dir, "slides", doc.name + "-" + doc.rev + ".txt")) as f:
137+
self.assertEqual(f.read(), content)
138+

ietf/doc/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,5 @@
102102
(r'^(?P<name>charter-[A-Za-z0-9._+-]+)/', include('ietf.doc.urls_charter')),
103103
(r'^(?P<name>[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')),
104104
(r'^(?P<name>[A-Za-z0-9._+-]+)/status-change/', include('ietf.doc.urls_status_change')),
105+
(r'^(?P<name>[A-Za-z0-9._+-]+)/material/', include('ietf.doc.urls_material')),
105106
)

ietf/doc/urls_material.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.conf.urls import patterns, url
2+
3+
urlpatterns = patterns('ietf.doc.views_material',
4+
url(r'^(?P<action>state|title|revise)/$', "edit_material", name="material_edit"),
5+
)
6+

ietf/doc/utils.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from django.conf import settings
77
from django.db.models.query import EmptyQuerySet
8+
from django.forms import ValidationError
89

910
from ietf.utils import markup_txt
1011
from ietf.doc.models import Document, DocHistory
@@ -228,16 +229,13 @@ def add_links_in_new_revision_events(doc, events, diff_revisions):
228229

229230

230231
def get_document_content(key, filename, split=True, markup=True):
231-
f = None
232232
try:
233-
f = open(filename, 'rb')
234-
raw_content = f.read()
233+
with open(filename, 'rb') as f:
234+
raw_content = f.read()
235235
except IOError:
236236
error = "Error; cannot read ("+key+")"
237237
return error
238-
finally:
239-
if f:
240-
f.close()
238+
241239
if markup:
242240
return markup_txt.markup(raw_content, split)
243241
else:
@@ -397,3 +395,18 @@ def rebuild_reference_relations(doc):
397395
ret['unfound']=list(unfound)
398396

399397
return ret
398+
399+
def check_common_doc_name_rules(name):
400+
"""Check common rules for document names for use in forms, throws
401+
ValidationError in case there's a problem."""
402+
403+
errors = []
404+
if re.search("[^a-z0-9-]", name):
405+
errors.append("The name may only contain digits, lowercase letters and dashes.")
406+
if re.search("--", name):
407+
errors.append("Please do not put more than one hyphen between any two words in the name.")
408+
if re.search("-[0-9]{2}$", name):
409+
errors.append("This name looks like ends in a version number. -00 will be added automatically. Please adjust the end of the name.")
410+
411+
if errors:
412+
raise ValidationError(errors)

ietf/doc/utils_charter.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
from ietf.utils.history import find_history_active_at
1010
from ietf.utils.mail import send_mail_text
1111

12+
def charter_name_for_group(group):
13+
if group.type_id == "rg":
14+
top_org = "irtf"
15+
else:
16+
top_org = "ietf"
17+
18+
return "charter-%s-%s" % (top_org, group.acronym)
1219

1320
def next_revision(rev):
1421
if rev == "":

0 commit comments

Comments
 (0)