Add simple materials page, tie up some of the loose ends in group generalization work
- Legacy-Id: 7768
This commit is contained in:
parent
d16bdb3723
commit
9579525ca4
|
@ -7,7 +7,7 @@ import shutil
|
|||
|
||||
from django import forms
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.http import HttpResponseForbidden, Http404
|
||||
from django.utils.html import mark_safe
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
@ -18,7 +18,8 @@ from ietf.doc.models import DocAlias, DocTagName, Document, State, save_document
|
|||
from ietf.doc.utils import get_tags_for_stream_id
|
||||
from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStateName,
|
||||
GroupStateTransitions, GroupTypeName, GroupURL, ChangeStateGroupEvent )
|
||||
from ietf.group.utils import save_group_in_history, can_manage_group_type
|
||||
from ietf.group.utils import (save_group_in_history, can_manage_group_type, can_manage_materials,
|
||||
get_group_or_404)
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.person.forms import EmailsField
|
||||
from ietf.person.models import Person, Email
|
||||
|
@ -166,6 +167,9 @@ def submit_initial_charter(request, group_type, acronym=None):
|
|||
return HttpResponseForbidden("You don't have permission to access this view")
|
||||
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
if not group.features.has_chartering_process:
|
||||
raise Http404
|
||||
|
||||
if not group.charter:
|
||||
group.charter = get_or_create_initial_charter(group, group_type)
|
||||
group.save()
|
||||
|
@ -188,6 +192,9 @@ def edit(request, group_type=None, acronym=None, action="edit"):
|
|||
else:
|
||||
raise Http404
|
||||
|
||||
if not group_type and group:
|
||||
group_type = group.type_id
|
||||
|
||||
if request.method == 'POST':
|
||||
form = GroupForm(request.POST, group=group, confirmed=request.POST.get("confirmed", False), group_type=group_type)
|
||||
if form.is_valid():
|
||||
|
@ -330,7 +337,7 @@ class ConcludeForm(forms.Form):
|
|||
@login_required
|
||||
def conclude(request, group_type, acronym):
|
||||
"""Request the closing of group, prompting for instructions."""
|
||||
group = get_object_or_404(Group, type=group_type, acronym=acronym)
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
|
||||
if not can_manage_group_type(request.user, group_type):
|
||||
return HttpResponseForbidden("You don't have permission to access this view")
|
||||
|
@ -357,7 +364,10 @@ def conclude(request, group_type, acronym):
|
|||
|
||||
@login_required
|
||||
def customize_workflow(request, group_type, acronym):
|
||||
group = get_object_or_404(Group, type=group_type, acronym=acronym)
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.customize_workflow:
|
||||
raise Http404
|
||||
|
||||
if (not has_role(request.user, "Secretariat") and
|
||||
not group.role_set.filter(name="chair", person__user=request.user)):
|
||||
return HttpResponseForbidden("You don't have permission to access this view")
|
||||
|
@ -445,3 +455,19 @@ def customize_workflow(request, group_type, acronym):
|
|||
'states': states,
|
||||
'tags': tags,
|
||||
})
|
||||
|
||||
@login_required
|
||||
def upload_materials(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_materials:
|
||||
raise Http404
|
||||
|
||||
if not can_manage_materials(request.user, group):
|
||||
return HttpResponseForbidden("You don't have permission to access this view")
|
||||
|
||||
# FIXME: fill in
|
||||
|
||||
return render(request, 'group/materials.html',
|
||||
construct_group_menu_context(request, group, "materials", group_type, {
|
||||
"materials": materials,
|
||||
}))
|
||||
|
|
|
@ -4,7 +4,8 @@ class GroupFeatures(object):
|
|||
|
||||
has_milestones = False
|
||||
has_chartering_process = False
|
||||
has_documents = False
|
||||
has_documents = False # i.e. drafts/RFCs
|
||||
has_materials = False
|
||||
customize_workflow = False
|
||||
default_tab = "group_charter"
|
||||
|
||||
|
@ -16,4 +17,4 @@ class GroupFeatures(object):
|
|||
self.customize_workflow = True
|
||||
self.default_tab = "group_docs"
|
||||
elif group.type_id in ("team",):
|
||||
self.has_documents = True
|
||||
self.has_materials = True
|
||||
|
|
|
@ -46,12 +46,13 @@ from django.db.models import Q
|
|||
from django.utils.safestring import mark_safe
|
||||
|
||||
from ietf.doc.views_search import SearchForm, retrieve_search_results
|
||||
from ietf.doc.models import State, DocAlias, RelatedDocument
|
||||
from ietf.doc.models import Document, State, DocAlias, RelatedDocument
|
||||
from ietf.doc.utils import get_chartering_type
|
||||
from ietf.doc.templatetags.ietf_filters import clean_whitespace
|
||||
from ietf.group.models import Group, Role, ChangeStateGroupEvent
|
||||
from ietf.name.models import GroupTypeName
|
||||
from ietf.group.utils import get_charter_text, can_manage_group_type, milestone_reviewer_for_group_type
|
||||
from ietf.group.utils import can_manage_materials, get_group_or_404
|
||||
from ietf.utils.pipe import pipe
|
||||
|
||||
def roles(group, role_name):
|
||||
|
@ -256,6 +257,9 @@ def concluded_groups(request):
|
|||
return render(request, 'group/concluded_groups.html',
|
||||
dict(group_types=group_types))
|
||||
|
||||
def get_group_materials(group):
|
||||
return Document.objects.filter(group=group).exclude(states__slug="deleted")#.exclude(session=None).exclude(type="charter")
|
||||
|
||||
def construct_group_menu_context(request, group, selected, group_type, others):
|
||||
"""Return context with info for the group menu filled in."""
|
||||
kwargs = dict(acronym=group.acronym)
|
||||
|
@ -264,11 +268,13 @@ def construct_group_menu_context(request, group, selected, group_type, others):
|
|||
|
||||
# entries
|
||||
entries = []
|
||||
if group.features().has_documents:
|
||||
if group.features.has_documents:
|
||||
entries.append(("Documents", urlreverse("ietf.group.info.group_documents", kwargs=kwargs)))
|
||||
entries.append(("Charter", urlreverse("ietf.group.info.group_charter", kwargs=kwargs)))
|
||||
if group.features.has_materials and get_group_materials(group).exists():
|
||||
entries.append(("Materials", urlreverse("ietf.group.info.materials", kwargs=kwargs)))
|
||||
entries.append(("History", urlreverse("ietf.group.info.history", kwargs=kwargs)))
|
||||
if group.features().has_documents:
|
||||
if group.features.has_documents:
|
||||
entries.append(("Dependency Graph", urlreverse("ietf.group.info.dependencies_pdf", kwargs=kwargs)))
|
||||
|
||||
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):
|
|||
is_chair = group.has_role(request.user, "chair")
|
||||
can_manage = can_manage_group_type(request.user, group.type_id)
|
||||
|
||||
if group.features().has_milestones:
|
||||
if group.features.has_milestones:
|
||||
if group.state_id != "proposed" and (is_chair or can_manage):
|
||||
actions.append((u"Add or edit milestones", urlreverse("group_edit_milestones", kwargs=kwargs)))
|
||||
|
||||
if group.features.has_materials and can_manage_materials(request.user, group):
|
||||
actions.append((u"Upload materials", urlreverse("group_upload_materials", kwargs=kwargs)))
|
||||
|
||||
if group.state_id != "conclude" and can_manage:
|
||||
actions.append((u"Edit group", urlreverse("group_edit", kwargs=kwargs)))
|
||||
|
||||
if group.features().customize_workflow and (is_chair or can_manage):
|
||||
if group.features.customize_workflow and (is_chair or can_manage):
|
||||
actions.append((u"Customize workflow", urlreverse("ietf.group.edit.customize_workflow", kwargs=kwargs)))
|
||||
|
||||
if group.state_id in ("active", "dormant") and can_manage:
|
||||
|
@ -341,23 +350,17 @@ def search_for_group_documents(group):
|
|||
|
||||
return docs, meta, docs_related, meta_related
|
||||
|
||||
def get_group_or_404(acronym, group_type):
|
||||
"""Helper to overcome the schism between group-type prefixed URLs and generic."""
|
||||
possible_groups = Group.objects.all()
|
||||
if group_type:
|
||||
possible_groups = possible_groups.filter(type=group_type)
|
||||
|
||||
return get_object_or_404(possible_groups, acronym=acronym)
|
||||
|
||||
def group_home(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
kwargs = dict(acronym=group.acronym)
|
||||
if group_type:
|
||||
kwargs["group_type"] = group_type
|
||||
return HttpResponseRedirect(urlreverse(group.features().default_tab, kwargs=kwargs))
|
||||
return HttpResponseRedirect(urlreverse(group.features.default_tab, kwargs=kwargs))
|
||||
|
||||
def group_documents(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_documents:
|
||||
raise Http404
|
||||
|
||||
docs, meta, docs_related, meta_related = search_for_group_documents(group)
|
||||
|
||||
|
@ -372,6 +375,8 @@ def group_documents(request, acronym, group_type=None):
|
|||
def group_documents_txt(request, acronym, group_type=None):
|
||||
"""Return tabulator-separated rows with documents for group."""
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_documents:
|
||||
raise Http404
|
||||
|
||||
docs, meta, docs_related, meta_related = search_for_group_documents(group)
|
||||
|
||||
|
@ -393,7 +398,6 @@ def group_documents_txt(request, acronym, group_type=None):
|
|||
|
||||
return HttpResponse(u"\n".join(rows), content_type='text/plain; charset=UTF-8')
|
||||
|
||||
|
||||
def group_charter(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
|
||||
|
@ -410,10 +414,10 @@ def group_charter(request, acronym, group_type=None):
|
|||
|
||||
can_manage = can_manage_group_type(request.user, group.type_id)
|
||||
|
||||
if group.features().has_chartering_process:
|
||||
if group.features.has_chartering_process:
|
||||
description = group.charter_text
|
||||
else:
|
||||
description = group.description or "No description entered yet."
|
||||
description = group.description or "No description yet."
|
||||
|
||||
return render(request, 'group/group_charter.html',
|
||||
construct_group_menu_context(request, group, "charter", group_type, {
|
||||
|
@ -433,10 +437,21 @@ def history(request, acronym, group_type=None):
|
|||
|
||||
return render(request, 'group/history.html',
|
||||
construct_group_menu_context(request, group, "history", group_type, {
|
||||
"events": events,
|
||||
}))
|
||||
|
||||
|
||||
"events": events,
|
||||
}))
|
||||
|
||||
def materials(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_materials:
|
||||
raise Http404
|
||||
|
||||
materials = get_group_materials(group).order_by("-time")
|
||||
|
||||
return render(request, 'group/materials.html',
|
||||
construct_group_menu_context(request, group, "materials", group_type, {
|
||||
"materials": materials,
|
||||
}))
|
||||
|
||||
def nodename(name):
|
||||
return name.replace('-','_')
|
||||
|
||||
|
@ -556,6 +571,8 @@ def make_dot(group):
|
|||
|
||||
def dependencies_dot(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_documents:
|
||||
raise Http404
|
||||
|
||||
return HttpResponse(make_dot(group),
|
||||
content_type='text/plain; charset=UTF-8'
|
||||
|
@ -564,6 +581,8 @@ def dependencies_dot(request, acronym, group_type=None):
|
|||
@cache_page ( 60 * 60 )
|
||||
def dependencies_pdf(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_documents:
|
||||
raise Http404
|
||||
|
||||
dothandle,dotname = mkstemp()
|
||||
os.close(dothandle)
|
||||
|
|
|
@ -12,7 +12,8 @@ from django.contrib.auth.decorators import login_required
|
|||
from ietf.doc.models import Document, DocEvent
|
||||
from ietf.doc.utils import get_chartering_type
|
||||
from ietf.group.models import Group, GroupMilestone, MilestoneGroupEvent
|
||||
from ietf.group.utils import save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type
|
||||
from ietf.group.utils import (save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type,
|
||||
get_group_or_404)
|
||||
from ietf.name.models import GroupMilestoneStateName
|
||||
from ietf.group.mails import email_milestones_changed
|
||||
|
||||
|
@ -108,17 +109,19 @@ class MilestoneForm(forms.Form):
|
|||
return r
|
||||
|
||||
@login_required
|
||||
def edit_milestones(request, group_type, acronym, milestone_set="current"):
|
||||
def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
||||
# milestones_set + needs_review: we have several paths into this view
|
||||
# management (IRTF chair/AD/...)/Secr. -> all actions on current + add new
|
||||
# group chair -> limited actions on current + add new for review
|
||||
# (re)charter -> all actions on existing in state charter + add new in state charter
|
||||
#
|
||||
# For charters we store the history on the charter document to not confuse people.
|
||||
group = get_object_or_404(Group, type=group_type, acronym=acronym)
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_milestones:
|
||||
raise Http404
|
||||
|
||||
needs_review = False
|
||||
if not can_manage_group_type(request.user, group_type):
|
||||
if not can_manage_group_type(request.user, group.type_id):
|
||||
if group.role_set.filter(name="chair", person__user=request.user):
|
||||
if milestone_set == "current":
|
||||
needs_review = True
|
||||
|
@ -332,8 +335,10 @@ def edit_milestones(request, group_type, acronym, milestone_set="current"):
|
|||
@login_required
|
||||
def reset_charter_milestones(request, group_type, acronym):
|
||||
"""Reset charter milestones to the currently in-use milestones."""
|
||||
group = get_object_or_404(Group, type=group_type, acronym=acronym)
|
||||
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_milestones:
|
||||
raise Http404
|
||||
|
||||
if (not can_manage_group_type(request.user, group_type) and
|
||||
not group.role_set.filter(name="chair", person__user=request.user)):
|
||||
return HttpResponseForbidden("You are not chair of this group.")
|
||||
|
|
|
@ -80,9 +80,12 @@ class Group(GroupInfo):
|
|||
def bg_color(self):
|
||||
return bg_group_colors[self.upcase_acronym]
|
||||
|
||||
@property
|
||||
def features(self):
|
||||
from ietf.group.features import GroupFeatures
|
||||
return GroupFeatures(self)
|
||||
if not hasattr(self, "features_cache"):
|
||||
from ietf.group.features import GroupFeatures
|
||||
self.features_cache = GroupFeatures(self)
|
||||
return self.features_cache
|
||||
|
||||
def json_url(self):
|
||||
return "/group/%s.json" % (self.acronym,)
|
||||
|
|
|
@ -7,10 +7,13 @@ urlpatterns = patterns('',
|
|||
(r'^chartering/$', 'ietf.group.info.chartering_groups'),
|
||||
(r'^chartering/create/(?P<group_type>(wg|rg))/$', 'ietf.group.edit.edit', {'action': "charter"}, "group_create"),
|
||||
(r'^concluded/$', 'ietf.group.info.concluded_groups'),
|
||||
# FIXME: the remainder here is currently duplicated in urls_info.py, need to unify these at some point
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', 'ietf.group.info.group_home', None, "group_home"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/$', 'ietf.group.info.group_documents', None, "group_docs"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/charter/$', 'ietf.group.info.group_charter', None, 'group_charter'),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/history/$', 'ietf.group.info.history'),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/$', 'ietf.group.info.materials', None, "group_materials"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/upload/$', 'ietf.group.edit.upload_materials', None, "group_upload_materials"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/dot/$', 'ietf.group.info.dependencies_dot'),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/pdf/$', 'ietf.group.info.dependencies_pdf'),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/init-charter/', 'ietf.group.edit.submit_initial_charter'),
|
||||
|
|
|
@ -19,7 +19,8 @@ urlpatterns = patterns('',
|
|||
(r'^bofs/$', info.bofs),
|
||||
(r'^bofs/create/$', edit.edit, {'action': "create"}, "bof_create"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/txt/$', info.group_documents_txt),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', info.group_documents, None, "group_docs"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/$', info.group_home, None, "group_home"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/documents/$', info.group_documents, None, "group_docs"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/charter/$', info.group_charter, None, 'group_charter'),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/history/$', info.history),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/deps/dot/$', info.dependencies_dot),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from ietf.group.models import Group, RoleHistory
|
||||
from ietf.person.models import Email
|
||||
|
@ -107,3 +108,14 @@ def milestone_reviewer_for_group_type(group_type):
|
|||
else:
|
||||
return "Area Director"
|
||||
|
||||
def can_manage_materials(user, group):
|
||||
return group.has_role(user, ("chair", "delegate", "secr")) or has_role(user, 'Secretariat')
|
||||
|
||||
def get_group_or_404(acronym, group_type):
|
||||
"""Helper to overcome the schism between group-type prefixed URLs and generic."""
|
||||
possible_groups = Group.objects.all()
|
||||
if group_type:
|
||||
possible_groups = possible_groups.filter(type=group_type)
|
||||
|
||||
return get_object_or_404(possible_groups, acronym=acronym)
|
||||
|
||||
|
|
27
ietf/templates/group/materials.html
Normal file
27
ietf/templates/group/materials.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "group/group_base.html" %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block group_subtitle %}Materials{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
{{ block.super }}
|
||||
.material { margin-top: 0.5em; }
|
||||
{% endblock %}
|
||||
|
||||
{% block group_content %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
<h2>Materials</h2>
|
||||
|
||||
{% if materials %}
|
||||
{% for d in materials %}
|
||||
<div class="material">
|
||||
<a href="">{{ d.title }}</a>
|
||||
({{ d.time|date:"Y-m-d" }})
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>No materials uploaded.</p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in a new issue