Move group material handling code to doc/material/ now that the view of the material is integrated into /doc/, fix a bunch of bugs
- Legacy-Id: 7834
This commit is contained in:
parent
c94757405d
commit
88cf68d43f
|
@ -173,7 +173,7 @@ class DocumentInfo(models.Model):
|
|||
def meeting_related(self):
|
||||
return(self.type_id in ("agenda", "minutes", "slides") and (
|
||||
self.name.split("-")[1] == "interim"
|
||||
or self.session_set.exists()))
|
||||
or (self.session_set.exists() if isinstance(self, Document) else self.doc.session_set.exists())))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
|
@ -102,4 +102,5 @@ urlpatterns = patterns('',
|
|||
(r'^(?P<name>charter-[A-Za-z0-9._+-]+)/', include('ietf.doc.urls_charter')),
|
||||
(r'^(?P<name>[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')),
|
||||
(r'^(?P<name>[A-Za-z0-9._+-]+)/status-change/', include('ietf.doc.urls_status_change')),
|
||||
(r'^(?P<name>[A-Za-z0-9._+-]+)/material/', include('ietf.doc.urls_material')),
|
||||
)
|
||||
|
|
6
ietf/doc/urls_material.py
Normal file
6
ietf/doc/urls_material.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.conf.urls import patterns, url
|
||||
|
||||
urlpatterns = patterns('ietf.doc.views_material',
|
||||
url(r'^(?P<action>state|title|revise)/$', "edit_material", name="material_edit"),
|
||||
)
|
||||
|
168
ietf/doc/views_material.py
Normal file
168
ietf/doc/views_material.py
Normal file
|
@ -0,0 +1,168 @@
|
|||
# views for managing group materials (slides, ...)
|
||||
import re
|
||||
import os
|
||||
import datetime
|
||||
import shutil
|
||||
|
||||
from django import forms
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.http import HttpResponse, HttpResponseForbidden, Http404, HttpResponseRedirect
|
||||
from django.utils.html import mark_safe
|
||||
from django.utils.text import slugify
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.doc.models import Document, DocAlias, DocTypeName, DocEvent, State
|
||||
from ietf.doc.models import NewRevisionDocEvent, save_document_in_history
|
||||
from ietf.doc.utils import add_state_change_event
|
||||
from ietf.group.models import Group
|
||||
from ietf.group.utils import can_manage_materials
|
||||
from ietf.ietfauth.utils import has_role
|
||||
|
||||
@login_required
|
||||
def choose_material_type(request, acronym):
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
if not group.features.has_materials:
|
||||
raise Http404
|
||||
|
||||
return render(request, 'doc/material/choose_material_type.html', {
|
||||
'group': group,
|
||||
'material_types': DocTypeName.objects.filter(slug__in=group.features.material_types),
|
||||
})
|
||||
|
||||
def name_for_material(doc_type, group, title):
|
||||
return "%s-%s-%s" % (doc_type.slug, group.acronym, slugify(title))
|
||||
|
||||
class UploadMaterialForm(forms.Form):
|
||||
title = forms.CharField(max_length=Document._meta.get_field("title").max_length)
|
||||
state = forms.ModelChoiceField(State.objects.all(), empty_label=None)
|
||||
material = forms.FileField(label='File', help_text="PDF or text file (ASCII/UTF-8)")
|
||||
|
||||
def __init__(self, doc_type, action, group, doc, *args, **kwargs):
|
||||
super(UploadMaterialForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fields["state"].queryset = self.fields["state"].queryset.filter(type=doc_type)
|
||||
|
||||
self.doc_type = doc_type
|
||||
self.action = action
|
||||
self.group = group
|
||||
|
||||
if action == "new":
|
||||
self.fields["state"].widget = forms.HiddenInput()
|
||||
self.fields["state"].queryset = self.fields["state"].queryset.filter(slug="active")
|
||||
self.fields["state"].initial = self.fields["state"].queryset[0].pk
|
||||
else:
|
||||
self.fields["title"].initial = doc.title
|
||||
self.fields["state"].initial = doc.get_state().pk if doc.get_state() else None
|
||||
if doc.get_state_slug() == "deleted":
|
||||
self.fields["state"].help_text = "Note: If you wish to revise this document, you may wish to change the state so it's not deleted."
|
||||
|
||||
if action == "title":
|
||||
del self.fields["state"]
|
||||
del self.fields["material"]
|
||||
elif action == "state":
|
||||
del self.fields["title"]
|
||||
del self.fields["material"]
|
||||
|
||||
def clean_title(self):
|
||||
title = self.cleaned_data["title"]
|
||||
if self.action == "new":
|
||||
name = name_for_material(self.doc_type, self.group, title)
|
||||
existing = Document.objects.filter(type=self.doc_type, name=name)
|
||||
if existing:
|
||||
url = urlreverse("material_edit", kwargs={ 'name': existing[0].name, 'action': 'revise' })
|
||||
raise forms.ValidationError(mark_safe("Can't upload: %s with name %s already exists. The name is derived from the title so you must either choose another title for what you're uploading or <a href=\"%s\">revise the existing %s</a>." % (self.doc_type.name, name, url, name)))
|
||||
|
||||
return title
|
||||
|
||||
@login_required
|
||||
def edit_material(request, name=None, acronym=None, action=None, doc_type=None):
|
||||
# the materials process is not very developed, so at the moment we
|
||||
# handle everything through the same view/form
|
||||
|
||||
if action == "new":
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
if not group.features.has_materials:
|
||||
raise Http404
|
||||
|
||||
doc = None
|
||||
document_type = get_object_or_404(DocTypeName, slug=doc_type)
|
||||
else:
|
||||
doc = get_object_or_404(Document, name=name)
|
||||
group = doc.group
|
||||
document_type = doc.type
|
||||
|
||||
if not can_manage_materials(request.user, group):
|
||||
return HttpResponseForbidden("You don't have permission to access this view")
|
||||
|
||||
if request.method == 'POST':
|
||||
form = UploadMaterialForm(document_type, action, group, doc, request.POST, request.FILES)
|
||||
|
||||
if form.is_valid():
|
||||
if action == "new":
|
||||
doc = Document()
|
||||
doc.type = document_type
|
||||
doc.group = group
|
||||
doc.rev = "00"
|
||||
doc.name = name_for_material(doc.type, doc.group, form.cleaned_data["title"])
|
||||
prev_rev = None
|
||||
else:
|
||||
save_document_in_history(doc)
|
||||
prev_rev = doc.rev
|
||||
|
||||
prev_title = doc.title
|
||||
prev_state = doc.get_state()
|
||||
|
||||
if "title" in form.cleaned_data:
|
||||
doc.title = form.cleaned_data["title"]
|
||||
|
||||
doc.time = datetime.datetime.now()
|
||||
|
||||
if "material" in form.fields:
|
||||
if action != "new":
|
||||
doc.rev = "%02d" % (int(doc.rev) + 1)
|
||||
|
||||
f = form.cleaned_data["material"]
|
||||
file_ext = os.path.splitext(f.name)[1]
|
||||
|
||||
with open(os.path.join(doc.get_file_path(), doc.name + "-" + doc.rev + file_ext), 'wb+') as dest:
|
||||
for chunk in f.chunks():
|
||||
dest.write(chunk)
|
||||
|
||||
doc.save()
|
||||
|
||||
if action == "new":
|
||||
DocAlias.objects.get_or_create(name=doc.name, document=doc)
|
||||
|
||||
if prev_rev != doc.rev:
|
||||
e = NewRevisionDocEvent(type="new_revision", doc=doc, rev=doc.rev)
|
||||
e.time = doc.time
|
||||
e.by = request.user.person
|
||||
e.desc = "New version available: <b>%s-%s</b>" % (doc.name, doc.rev)
|
||||
e.save()
|
||||
|
||||
if prev_title != doc.title:
|
||||
e = DocEvent(doc=doc, by=request.user.person, type='changed_document')
|
||||
e.desc = u"Changed title to <b>%s</b>" % doc.title
|
||||
if prev_title:
|
||||
e.desc += u" from %s" % prev_title
|
||||
e.time = doc.time
|
||||
e.save()
|
||||
|
||||
if "state" in form.cleaned_data and form.cleaned_data["state"] != prev_state:
|
||||
doc.set_state(form.cleaned_data["state"])
|
||||
add_state_change_event(doc, request.user.person, prev_state, form.cleaned_data["state"])
|
||||
|
||||
return redirect("doc_view", name=doc.name)
|
||||
else:
|
||||
form = UploadMaterialForm(document_type, action, group, doc)
|
||||
|
||||
return render(request, 'doc/material/edit_material.html', {
|
||||
'group': group,
|
||||
'form': form,
|
||||
'action': action,
|
||||
'document_type': document_type,
|
||||
'doc_name': doc.name if doc else "",
|
||||
})
|
|
@ -459,136 +459,3 @@ def customize_workflow(request, group_type, acronym):
|
|||
'states': states,
|
||||
'tags': tags,
|
||||
})
|
||||
|
||||
@login_required
|
||||
def choose_material_type(request, acronym, group_type=None):
|
||||
group = get_group_or_404(acronym, group_type)
|
||||
if not group.features.has_materials:
|
||||
raise Http404
|
||||
|
||||
return render(request, 'group/choose_material_type.html', {
|
||||
'group': group,
|
||||
'material_types': DocTypeName.objects.filter(slug__in=group.features.material_types),
|
||||
})
|
||||
|
||||
def name_for_material(doc_type, group, title):
|
||||
return "%s-%s-%s" % (doc_type.slug, group.acronym, slugify(title))
|
||||
|
||||
class UploadMaterialForm(forms.Form):
|
||||
title = forms.CharField(max_length=Document._meta.get_field("title").max_length)
|
||||
state = forms.ModelChoiceField(State.objects.all(), empty_label=None)
|
||||
material = forms.FileField(label='File', help_text="PDF or text file (ASCII/UTF-8)")
|
||||
|
||||
def __init__(self, doc_type, action, group, doc, *args, **kwargs):
|
||||
super(UploadMaterialForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fields["state"].queryset = self.fields["state"].queryset.filter(type=doc_type)
|
||||
|
||||
self.doc_type = doc_type
|
||||
self.action = action
|
||||
self.group = group
|
||||
|
||||
if action == "new":
|
||||
self.fields["state"].widget = forms.HiddenInput()
|
||||
self.fields["state"].queryset = self.fields["state"].queryset.filter(slug="active")
|
||||
self.fields["state"].initial = self.fields["state"].queryset[0].pk
|
||||
else:
|
||||
self.fields["title"].initial = doc.title
|
||||
self.fields["state"].initial = doc.get_state().pk if doc.get_state() else None
|
||||
if doc.get_state_slug() == "deleted":
|
||||
self.fields["state"].help_text = "Note: If you wish to revise this document, you may wish to change the state so it's not deleted."
|
||||
|
||||
if action == "edit":
|
||||
del self.fields["material"]
|
||||
|
||||
def clean_title(self):
|
||||
title = self.cleaned_data["title"]
|
||||
if self.action == "new":
|
||||
name = name_for_material(self.doc_type, self.group, title)
|
||||
existing = Document.objects.filter(type=self.doc_type, name=name)
|
||||
if existing:
|
||||
url = urlreverse("group_revise_material", kwargs={ 'acronym': self.group.acronym, 'name': existing[0].name })
|
||||
raise forms.ValidationError(mark_safe("Can't upload: %s with name %s already exists. The name is derived from the title so you must either choose another title for what you're uploading or <a href=\"%s\">revise the existing %s</a>." % (self.doc_type.name, name, url, name)))
|
||||
|
||||
return title
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_material(request, acronym, action="new", name=None, doc_type=None, 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")
|
||||
|
||||
existing = None
|
||||
if name and action != "new":
|
||||
existing = get_object_or_404(Document, name=name)
|
||||
document_type = existing.type
|
||||
else:
|
||||
document_type = get_object_or_404(DocTypeName, slug=doc_type)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = UploadMaterialForm(document_type, action, group, existing, request.POST, request.FILES)
|
||||
|
||||
if form.is_valid():
|
||||
if action == "new":
|
||||
d = Document()
|
||||
d.type = document_type
|
||||
d.group = group
|
||||
d.rev = "00"
|
||||
d.name = name_for_material(d.type, d.group, form.cleaned_data["title"])
|
||||
else:
|
||||
d = existing
|
||||
|
||||
prev_rev = d.rev
|
||||
prev_title = d.title
|
||||
prev_state = d.get_state()
|
||||
|
||||
d.title = form.cleaned_data["title"]
|
||||
d.time = datetime.datetime.now()
|
||||
|
||||
if "material" in form.fields:
|
||||
if action != "new":
|
||||
d.rev = "%02d" % (int(d.rev) + 1)
|
||||
|
||||
f = form.cleaned_data["material"]
|
||||
file_ext = os.path.splitext(f.name)[1]
|
||||
|
||||
with open(os.path.join(d.get_file_path(), d.name + "-" + d.rev + file_ext), 'wb+') as dest:
|
||||
for chunk in f.chunks():
|
||||
dest.write(chunk)
|
||||
|
||||
d.save()
|
||||
|
||||
if action == "new":
|
||||
DocAlias.objects.get_or_create(name=d.name, document=d)
|
||||
|
||||
if not existing or prev_rev != d.rev:
|
||||
e = NewRevisionDocEvent(type="new_revision", doc=d, rev=d.rev)
|
||||
e.time = d.time
|
||||
e.by = request.user.person
|
||||
e.desc = "New version available: <b>%s-%s</b>" % (d.name, d.rev)
|
||||
e.save()
|
||||
|
||||
if prev_title != d.title:
|
||||
e = DocEvent(doc=d, by=request.user.person, type='changed_document')
|
||||
e.desc = u"Changed title to <b>%s</b>" % d.title
|
||||
if prev_title:
|
||||
e.desc += u" from %s" % prev_title
|
||||
e.time = d.time
|
||||
e.save()
|
||||
|
||||
d.set_state(form.cleaned_data["state"])
|
||||
add_state_change_event(d, request.user.person, prev_state, form.cleaned_data["state"])
|
||||
|
||||
return redirect("group_materials", acronym=group.acronym)
|
||||
else:
|
||||
form = UploadMaterialForm(document_type, action, group, existing)
|
||||
|
||||
return render(request, 'group/edit_material.html', {
|
||||
'group': group,
|
||||
'form': form,
|
||||
'action': action,
|
||||
})
|
||||
|
|
|
@ -299,7 +299,7 @@ def construct_group_menu_context(request, group, selected, group_type, others):
|
|||
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 material", urlreverse("group_choose_material_type", kwargs=kwargs)))
|
||||
actions.append((u"Upload material", urlreverse("ietf.doc.views_material.choose_material_type", kwargs=kwargs)))
|
||||
|
||||
if group.type_id in ("rg", "wg") and group.state_id != "conclude" and can_manage:
|
||||
actions.append((u"Edit group", urlreverse("group_edit", kwargs=kwargs)))
|
||||
|
|
|
@ -7,7 +7,9 @@ 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 things below are duplicated in urls_info.py, need to unify these at some point
|
||||
# FIXME: the things below are duplicated in urls_info.py while we
|
||||
# figure out whether to serve everything from /group/<acronym>,
|
||||
# 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_about', None, 'group_charter'),
|
||||
|
@ -25,10 +27,8 @@ urlpatterns = patterns('',
|
|||
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/about/(?P<group_type>.)?$', 'ietf.group.info.group_about', None, 'group_about'),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/$', 'ietf.group.info.materials', None, "group_materials"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/$', 'ietf.group.edit.choose_material_type', None, "group_choose_material_type"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/(?P<doc_type>[\w-]+)/$', 'ietf.group.edit.edit_material', { 'action': "new" }, "group_new_material"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/(?P<name>[^/]+)/edit/$', 'ietf.group.edit.edit_material', { 'action': "edit" }, "group_edit_material"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/(?P<name>[^/]+)/revise/$', 'ietf.group.edit.edit_material', { 'action': "revise" }, "group_revise_material"),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/$', 'ietf.doc.views_material.choose_material_type'),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/materials/new/(?P<doc_type>[\w-]+)/$', 'ietf.doc.views_material.edit_material', { 'action': "new" }, "group_new_material"),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -22,14 +22,14 @@
|
|||
<tr>
|
||||
<td>Title:</td>
|
||||
<td>
|
||||
<a {% if not snapshot and can_manage_material %} class="editlink" href="{% url "status_change_change_state" name=doc.name %}"{% endif %}>{{ doc.title }}</a>
|
||||
<a {% if not snapshot and can_manage_material %} class="editlink" href="{% url "material_edit" name=doc.name action="title" name=doc.name %}"{% endif %}>{{ doc.title }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>State:</td>
|
||||
<td>
|
||||
<a title="{{ doc.get_state.desc }}"{% if not snapshot and can_manage_material %} class="editlink" href="{% url "status_change_change_state" name=doc.name %}"{% endif %}>{{ doc.get_state.name }}</a>
|
||||
<a title="{{ doc.get_state.desc }}"{% if not snapshot and can_manage_material %} class="editlink" href="{% url "material_edit" name=doc.name action="state" %}"{% endif %}>{{ doc.get_state.name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -51,9 +51,7 @@
|
|||
|
||||
{% if not snapshot and can_manage_material %}
|
||||
<tr><td colspan="2">
|
||||
<a class="button" href="{% url "group_edit_material" acronym=group.acronym name=d.name %}">Change title/state</a>
|
||||
|
||||
<a class="button" href="{% url "group_revise_material" acronym=group.acronym name=d.name %}">Revise content</a>
|
||||
<a class="button" href="{% url "material_edit" name=doc.name action="revise" %}">Upload New Revision</a>
|
||||
</td><tr/>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Upload Material for Group {{ group.acronym }}{% endblock %}
|
||||
{% block title %}{% if action == "new" or action == "revise" %}Upload{% else %}Edit{% endif %} {{ document_type.name }} for Group {{ group.acronym }}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
{{ block.super }}
|
||||
|
@ -12,7 +12,7 @@ form.upload-material .submit-row td { padding-top: 1em; text-align: right; }
|
|||
{% block content %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
<h1>Upload Material for Group {{ group.acronym }}</h1>
|
||||
<h1>{% if action == "new" or action == "revise" %}Upload{% else %}Edit{% endif %} {{ document_type.name }} for Group {{ group.acronym }}</h1>
|
||||
|
||||
{% if action == "new" %}
|
||||
<p>
|
||||
|
@ -20,19 +20,16 @@ form.upload-material .submit-row td { padding-top: 1em; text-align: right; }
|
|||
<a href="{% url "group_materials" acronym=group.acronym %}">{{ group.acronym }}</a>.
|
||||
The file will appear under the materials tab in the group pages.
|
||||
</p>
|
||||
{% elif action == "edit" %}
|
||||
<p>
|
||||
Below you can edit the details of the material for the group
|
||||
<a href="{% url "group_materials" acronym=group.acronym %}">{{ group.acronym }}</a>.
|
||||
</p>
|
||||
|
||||
<h3>Upload</h3>
|
||||
{% elif action == "revise" %}
|
||||
<p>
|
||||
Below you can upload a new revision of the material for the group
|
||||
Below you can upload a new revision of {{ doc_name }} for the group
|
||||
<a href="{% url "group_materials" acronym=group.acronym %}">{{ group.acronym }}</a>.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h3>Upload</h3>
|
||||
<h3>Upload New Revision</h3>
|
||||
{% endif %}
|
||||
|
||||
<form class="upload-material" method="post" enctype="multipart/form-data">{% csrf_token %}
|
||||
<table>
|
||||
|
@ -40,8 +37,8 @@ form.upload-material .submit-row td { padding-top: 1em; text-align: right; }
|
|||
|
||||
<tr class="submit-row">
|
||||
<td colspan="2">
|
||||
<a class="button" href="{% url "group_materials" acronym=group.acronym %}">Cancel</a>
|
||||
<input class="submit button" type="submit" value="{% if action == "edit" %}Save{% else %}Upload{% endif %}" />
|
||||
<a class="button" href="{% if doc_name %}{% url "doc_view" name=doc_name %}{% else %}{% url "group_materials" acronym=group.acronym %}{% endif %}">Cancel</a>
|
||||
<input class="submit button" type="submit" value="{% if action == "new" or action == "revise" %}Upload{% else %}Save{% endif %}" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
Loading…
Reference in a new issue