Skip to content

Commit 9579525

Browse files
committed
Add simple materials page, tie up some of the loose ends in group generalization work
- Legacy-Id: 7768
1 parent d16bdb3 commit 9579525

File tree

9 files changed

+133
-36
lines changed

9 files changed

+133
-36
lines changed

ietf/group/edit.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from django import forms
99
from django.shortcuts import render, get_object_or_404, redirect
10-
from django.http import HttpResponseForbidden
10+
from django.http import HttpResponseForbidden, Http404
1111
from django.utils.html import mark_safe
1212
from django.http import Http404, HttpResponse
1313
from django.contrib.auth.decorators import login_required
@@ -18,7 +18,8 @@
1818
from ietf.doc.utils import get_tags_for_stream_id
1919
from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStateName,
2020
GroupStateTransitions, GroupTypeName, GroupURL, ChangeStateGroupEvent )
21-
from ietf.group.utils import save_group_in_history, can_manage_group_type
21+
from ietf.group.utils import (save_group_in_history, can_manage_group_type, can_manage_materials,
22+
get_group_or_404)
2223
from ietf.ietfauth.utils import has_role
2324
from ietf.person.forms import EmailsField
2425
from ietf.person.models import Person, Email
@@ -166,6 +167,9 @@ def submit_initial_charter(request, group_type, acronym=None):
166167
return HttpResponseForbidden("You don't have permission to access this view")
167168

168169
group = get_object_or_404(Group, acronym=acronym)
170+
if not group.features.has_chartering_process:
171+
raise Http404
172+
169173
if not group.charter:
170174
group.charter = get_or_create_initial_charter(group, group_type)
171175
group.save()
@@ -188,6 +192,9 @@ def edit(request, group_type=None, acronym=None, action="edit"):
188192
else:
189193
raise Http404
190194

195+
if not group_type and group:
196+
group_type = group.type_id
197+
191198
if request.method == 'POST':
192199
form = GroupForm(request.POST, group=group, confirmed=request.POST.get("confirmed", False), group_type=group_type)
193200
if form.is_valid():
@@ -330,7 +337,7 @@ class ConcludeForm(forms.Form):
330337
@login_required
331338
def conclude(request, group_type, acronym):
332339
"""Request the closing of group, prompting for instructions."""
333-
group = get_object_or_404(Group, type=group_type, acronym=acronym)
340+
group = get_group_or_404(acronym, group_type)
334341

335342
if not can_manage_group_type(request.user, group_type):
336343
return HttpResponseForbidden("You don't have permission to access this view")
@@ -357,7 +364,10 @@ def conclude(request, group_type, acronym):
357364

358365
@login_required
359366
def customize_workflow(request, group_type, acronym):
360-
group = get_object_or_404(Group, type=group_type, acronym=acronym)
367+
group = get_group_or_404(acronym, group_type)
368+
if not group.features.customize_workflow:
369+
raise Http404
370+
361371
if (not has_role(request.user, "Secretariat") and
362372
not group.role_set.filter(name="chair", person__user=request.user)):
363373
return HttpResponseForbidden("You don't have permission to access this view")
@@ -445,3 +455,19 @@ def customize_workflow(request, group_type, acronym):
445455
'states': states,
446456
'tags': tags,
447457
})
458+
459+
@login_required
460+
def upload_materials(request, acronym, group_type=None):
461+
group = get_group_or_404(acronym, group_type)
462+
if not group.features.has_materials:
463+
raise Http404
464+
465+
if not can_manage_materials(request.user, group):
466+
return HttpResponseForbidden("You don't have permission to access this view")
467+
468+
# FIXME: fill in
469+
470+
return render(request, 'group/materials.html',
471+
construct_group_menu_context(request, group, "materials", group_type, {
472+
"materials": materials,
473+
}))

ietf/group/features.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ class GroupFeatures(object):
44

55
has_milestones = False
66
has_chartering_process = False
7-
has_documents = False
7+
has_documents = False # i.e. drafts/RFCs
8+
has_materials = False
89
customize_workflow = False
910
default_tab = "group_charter"
1011

@@ -16,4 +17,4 @@ def __init__(self, group):
1617
self.customize_workflow = True
1718
self.default_tab = "group_docs"
1819
elif group.type_id in ("team",):
19-
self.has_documents = True
20+
self.has_materials = True

ietf/group/info.py

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,13 @@
4646
from django.utils.safestring import mark_safe
4747

4848
from ietf.doc.views_search import SearchForm, retrieve_search_results
49-
from ietf.doc.models import State, DocAlias, RelatedDocument
49+
from ietf.doc.models import Document, State, DocAlias, RelatedDocument
5050
from ietf.doc.utils import get_chartering_type
5151
from ietf.doc.templatetags.ietf_filters import clean_whitespace
5252
from ietf.group.models import Group, Role, ChangeStateGroupEvent
5353
from ietf.name.models import GroupTypeName
5454
from ietf.group.utils import get_charter_text, can_manage_group_type, milestone_reviewer_for_group_type
55+
from ietf.group.utils import can_manage_materials, get_group_or_404
5556
from ietf.utils.pipe import pipe
5657

5758
def roles(group, role_name):
@@ -256,6 +257,9 @@ def concluded_groups(request):
256257
return render(request, 'group/concluded_groups.html',
257258
dict(group_types=group_types))
258259

260+
def get_group_materials(group):
261+
return Document.objects.filter(group=group).exclude(states__slug="deleted")#.exclude(session=None).exclude(type="charter")
262+
259263
def construct_group_menu_context(request, group, selected, group_type, others):
260264
"""Return context with info for the group menu filled in."""
261265
kwargs = dict(acronym=group.acronym)
@@ -264,11 +268,13 @@ def construct_group_menu_context(request, group, selected, group_type, others):
264268

265269
# entries
266270
entries = []
267-
if group.features().has_documents:
271+
if group.features.has_documents:
268272
entries.append(("Documents", urlreverse("ietf.group.info.group_documents", kwargs=kwargs)))
269273
entries.append(("Charter", urlreverse("ietf.group.info.group_charter", kwargs=kwargs)))
274+
if group.features.has_materials and get_group_materials(group).exists():
275+
entries.append(("Materials", urlreverse("ietf.group.info.materials", kwargs=kwargs)))
270276
entries.append(("History", urlreverse("ietf.group.info.history", kwargs=kwargs)))
271-
if group.features().has_documents:
277+
if group.features.has_documents:
272278
entries.append(("Dependency Graph", urlreverse("ietf.group.info.dependencies_pdf", kwargs=kwargs)))
273279

274280
if group.list_archive.startswith("http:") or group.list_archive.startswith("https:") or group.list_archive.startswith("ftp:"):
@@ -283,14 +289,17 @@ def construct_group_menu_context(request, group, selected, group_type, others):
283289
is_chair = group.has_role(request.user, "chair")
284290
can_manage = can_manage_group_type(request.user, group.type_id)
285291

286-
if group.features().has_milestones:
292+
if group.features.has_milestones:
287293
if group.state_id != "proposed" and (is_chair or can_manage):
288294
actions.append((u"Add or edit milestones", urlreverse("group_edit_milestones", kwargs=kwargs)))
289295

296+
if group.features.has_materials and can_manage_materials(request.user, group):
297+
actions.append((u"Upload materials", urlreverse("group_upload_materials", kwargs=kwargs)))
298+
290299
if group.state_id != "conclude" and can_manage:
291300
actions.append((u"Edit group", urlreverse("group_edit", kwargs=kwargs)))
292301

293-
if group.features().customize_workflow and (is_chair or can_manage):
302+
if group.features.customize_workflow and (is_chair or can_manage):
294303
actions.append((u"Customize workflow", urlreverse("ietf.group.edit.customize_workflow", kwargs=kwargs)))
295304

296305
if group.state_id in ("active", "dormant") and can_manage:
@@ -341,23 +350,17 @@ def search_for_group_documents(group):
341350

342351
return docs, meta, docs_related, meta_related
343352

344-
def get_group_or_404(acronym, group_type):
345-
"""Helper to overcome the schism between group-type prefixed URLs and generic."""
346-
possible_groups = Group.objects.all()
347-
if group_type:
348-
possible_groups = possible_groups.filter(type=group_type)
349-
350-
return get_object_or_404(possible_groups, acronym=acronym)
351-
352353
def group_home(request, acronym, group_type=None):
353354
group = get_group_or_404(acronym, group_type)
354355
kwargs = dict(acronym=group.acronym)
355356
if group_type:
356357
kwargs["group_type"] = group_type
357-
return HttpResponseRedirect(urlreverse(group.features().default_tab, kwargs=kwargs))
358+
return HttpResponseRedirect(urlreverse(group.features.default_tab, kwargs=kwargs))
358359

359360
def group_documents(request, acronym, group_type=None):
360361
group = get_group_or_404(acronym, group_type)
362+
if not group.features.has_documents:
363+
raise Http404
361364

362365
docs, meta, docs_related, meta_related = search_for_group_documents(group)
363366

@@ -372,6 +375,8 @@ def group_documents(request, acronym, group_type=None):
372375
def group_documents_txt(request, acronym, group_type=None):
373376
"""Return tabulator-separated rows with documents for group."""
374377
group = get_group_or_404(acronym, group_type)
378+
if not group.features.has_documents:
379+
raise Http404
375380

376381
docs, meta, docs_related, meta_related = search_for_group_documents(group)
377382

@@ -393,7 +398,6 @@ def group_documents_txt(request, acronym, group_type=None):
393398

394399
return HttpResponse(u"\n".join(rows), content_type='text/plain; charset=UTF-8')
395400

396-
397401
def group_charter(request, acronym, group_type=None):
398402
group = get_group_or_404(acronym, group_type)
399403

@@ -410,10 +414,10 @@ def group_charter(request, acronym, group_type=None):
410414

411415
can_manage = can_manage_group_type(request.user, group.type_id)
412416

413-
if group.features().has_chartering_process:
417+
if group.features.has_chartering_process:
414418
description = group.charter_text
415419
else:
416-
description = group.description or "No description entered yet."
420+
description = group.description or "No description yet."
417421

418422
return render(request, 'group/group_charter.html',
419423
construct_group_menu_context(request, group, "charter", group_type, {
@@ -433,10 +437,21 @@ def history(request, acronym, group_type=None):
433437

434438
return render(request, 'group/history.html',
435439
construct_group_menu_context(request, group, "history", group_type, {
436-
"events": events,
437-
}))
438-
439-
440+
"events": events,
441+
}))
442+
443+
def materials(request, acronym, group_type=None):
444+
group = get_group_or_404(acronym, group_type)
445+
if not group.features.has_materials:
446+
raise Http404
447+
448+
materials = get_group_materials(group).order_by("-time")
449+
450+
return render(request, 'group/materials.html',
451+
construct_group_menu_context(request, group, "materials", group_type, {
452+
"materials": materials,
453+
}))
454+
440455
def nodename(name):
441456
return name.replace('-','_')
442457

@@ -556,6 +571,8 @@ def make_dot(group):
556571

557572
def dependencies_dot(request, acronym, group_type=None):
558573
group = get_group_or_404(acronym, group_type)
574+
if not group.features.has_documents:
575+
raise Http404
559576

560577
return HttpResponse(make_dot(group),
561578
content_type='text/plain; charset=UTF-8'
@@ -564,6 +581,8 @@ def dependencies_dot(request, acronym, group_type=None):
564581
@cache_page ( 60 * 60 )
565582
def dependencies_pdf(request, acronym, group_type=None):
566583
group = get_group_or_404(acronym, group_type)
584+
if not group.features.has_documents:
585+
raise Http404
567586

568587
dothandle,dotname = mkstemp()
569588
os.close(dothandle)

ietf/group/milestones.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
from ietf.doc.models import Document, DocEvent
1313
from ietf.doc.utils import get_chartering_type
1414
from ietf.group.models import Group, GroupMilestone, MilestoneGroupEvent
15-
from ietf.group.utils import save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type
15+
from ietf.group.utils import (save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type,
16+
get_group_or_404)
1617
from ietf.name.models import GroupMilestoneStateName
1718
from ietf.group.mails import email_milestones_changed
1819

@@ -108,17 +109,19 @@ def clean_resolved(self):
108109
return r
109110

110111
@login_required
111-
def edit_milestones(request, group_type, acronym, milestone_set="current"):
112+
def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
112113
# milestones_set + needs_review: we have several paths into this view
113114
# management (IRTF chair/AD/...)/Secr. -> all actions on current + add new
114115
# group chair -> limited actions on current + add new for review
115116
# (re)charter -> all actions on existing in state charter + add new in state charter
116117
#
117118
# For charters we store the history on the charter document to not confuse people.
118-
group = get_object_or_404(Group, type=group_type, acronym=acronym)
119+
group = get_group_or_404(acronym, group_type)
120+
if not group.features.has_milestones:
121+
raise Http404
119122

120123
needs_review = False
121-
if not can_manage_group_type(request.user, group_type):
124+
if not can_manage_group_type(request.user, group.type_id):
122125
if group.role_set.filter(name="chair", person__user=request.user):
123126
if milestone_set == "current":
124127
needs_review = True
@@ -332,8 +335,10 @@ def save_milestone_form(f):
332335
@login_required
333336
def reset_charter_milestones(request, group_type, acronym):
334337
"""Reset charter milestones to the currently in-use milestones."""
335-
group = get_object_or_404(Group, type=group_type, acronym=acronym)
336-
338+
group = get_group_or_404(acronym, group_type)
339+
if not group.features.has_milestones:
340+
raise Http404
341+
337342
if (not can_manage_group_type(request.user, group_type) and
338343
not group.role_set.filter(name="chair", person__user=request.user)):
339344
return HttpResponseForbidden("You are not chair of this group.")

ietf/group/models.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,12 @@ def fg_color(self):
8080
def bg_color(self):
8181
return bg_group_colors[self.upcase_acronym]
8282

83+
@property
8384
def features(self):
84-
from ietf.group.features import GroupFeatures
85-
return GroupFeatures(self)
85+
if not hasattr(self, "features_cache"):
86+
from ietf.group.features import GroupFeatures
87+
self.features_cache = GroupFeatures(self)
88+
return self.features_cache
8689

8790
def json_url(self):
8891
return "/group/%s.json" % (self.acronym,)

ietf/group/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
(r'^chartering/$', 'ietf.group.info.chartering_groups'),
88
(r'^chartering/create/(?P<group_type>(wg|rg))/$', 'ietf.group.edit.edit', {'action': "charter"}, "group_create"),
99
(r'^concluded/$', 'ietf.group.info.concluded_groups'),
10+
# FIXME: the remainder here is currently duplicated in urls_info.py, need to unify these at some point
1011
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', 'ietf.group.info.group_home', None, "group_home"),
1112
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/$', 'ietf.group.info.group_documents', None, "group_docs"),
1213
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/charter/$', 'ietf.group.info.group_charter', None, 'group_charter'),
1314
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/history/$', 'ietf.group.info.history'),
15+
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/$', 'ietf.group.info.materials', None, "group_materials"),
16+
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/upload/$', 'ietf.group.edit.upload_materials', None, "group_upload_materials"),
1417
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/dot/$', 'ietf.group.info.dependencies_dot'),
1518
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/pdf/$', 'ietf.group.info.dependencies_pdf'),
1619
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/init-charter/', 'ietf.group.edit.submit_initial_charter'),

ietf/group/urls_info.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
(r'^bofs/$', info.bofs),
2020
(r'^bofs/create/$', edit.edit, {'action': "create"}, "bof_create"),
2121
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/txt/$', info.group_documents_txt),
22-
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', info.group_documents, None, "group_docs"),
22+
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', info.group_home, None, "group_home"),
23+
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/$', info.group_documents, None, "group_docs"),
2324
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/charter/$', info.group_charter, None, 'group_charter'),
2425
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/history/$', info.history),
2526
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/dot/$', info.dependencies_dot),

ietf/group/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22

33
from django.conf import settings
4+
from django.shortcuts import get_object_or_404
45

56
from ietf.group.models import Group, RoleHistory
67
from ietf.person.models import Email
@@ -107,3 +108,14 @@ def milestone_reviewer_for_group_type(group_type):
107108
else:
108109
return "Area Director"
109110

111+
def can_manage_materials(user, group):
112+
return group.has_role(user, ("chair", "delegate", "secr")) or has_role(user, 'Secretariat')
113+
114+
def get_group_or_404(acronym, group_type):
115+
"""Helper to overcome the schism between group-type prefixed URLs and generic."""
116+
possible_groups = Group.objects.all()
117+
if group_type:
118+
possible_groups = possible_groups.filter(type=group_type)
119+
120+
return get_object_or_404(possible_groups, acronym=acronym)
121+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{% extends "group/group_base.html" %}
2+
{% load ietf_filters %}
3+
4+
{% block group_subtitle %}Materials{% endblock %}
5+
6+
{% block morecss %}
7+
{{ block.super }}
8+
.material { margin-top: 0.5em; }
9+
{% endblock %}
10+
11+
{% block group_content %}
12+
{% load ietf_filters %}
13+
14+
<h2>Materials</h2>
15+
16+
{% if materials %}
17+
{% for d in materials %}
18+
<div class="material">
19+
<a href="">{{ d.title }}</a>
20+
({{ d.time|date:"Y-m-d" }})
21+
</div>
22+
{% endfor %}
23+
{% else %}
24+
<p>No materials uploaded.</p>
25+
{% endif %}
26+
27+
{% endblock %}

0 commit comments

Comments
 (0)