From 88cf68d43f84a6e943d2481f4591ad5e1d2a94fc Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 3 Jun 2014 09:42:16 +0000 Subject: [PATCH] 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 --- ietf/doc/models.py | 2 +- ietf/doc/urls.py | 1 + ietf/doc/urls_material.py | 6 + ietf/doc/views_material.py | 168 ++++++++++++++++++ ietf/group/edit.py | 133 -------------- ietf/group/info.py | 2 +- ietf/group/urls.py | 10 +- ietf/templates/doc/document_material.html | 8 +- .../material}/choose_material_type.html | 0 .../material}/edit_material.html | 21 +-- 10 files changed, 194 insertions(+), 157 deletions(-) create mode 100644 ietf/doc/urls_material.py create mode 100644 ietf/doc/views_material.py rename ietf/templates/{group => doc/material}/choose_material_type.html (100%) rename ietf/templates/{group => doc/material}/edit_material.html (59%) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index e70af5db4..10fd3a00d 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -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 diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index aba6daa89..9d2ea1086 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -102,4 +102,5 @@ urlpatterns = patterns('', (r'^(?Pcharter-[A-Za-z0-9._+-]+)/', include('ietf.doc.urls_charter')), (r'^(?P[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')), (r'^(?P[A-Za-z0-9._+-]+)/status-change/', include('ietf.doc.urls_status_change')), + (r'^(?P[A-Za-z0-9._+-]+)/material/', include('ietf.doc.urls_material')), ) diff --git a/ietf/doc/urls_material.py b/ietf/doc/urls_material.py new file mode 100644 index 000000000..01aa922fd --- /dev/null +++ b/ietf/doc/urls_material.py @@ -0,0 +1,6 @@ +from django.conf.urls import patterns, url + +urlpatterns = patterns('ietf.doc.views_material', + url(r'^(?Pstate|title|revise)/$', "edit_material", name="material_edit"), +) + diff --git a/ietf/doc/views_material.py b/ietf/doc/views_material.py new file mode 100644 index 000000000..4e462fb6e --- /dev/null +++ b/ietf/doc/views_material.py @@ -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 revise the existing %s." % (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: %s-%s" % (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 %s" % 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 "", + }) diff --git a/ietf/group/edit.py b/ietf/group/edit.py index 6e8200193..7e3739acb 100644 --- a/ietf/group/edit.py +++ b/ietf/group/edit.py @@ -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 revise the existing %s." % (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: %s-%s" % (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 %s" % 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, - }) diff --git a/ietf/group/info.py b/ietf/group/info.py index 727cc5d44..332a2c5d6 100644 --- a/ietf/group/info.py +++ b/ietf/group/info.py @@ -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))) diff --git a/ietf/group/urls.py b/ietf/group/urls.py index 4afb28fc9..283eb38cd 100644 --- a/ietf/group/urls.py +++ b/ietf/group/urls.py @@ -7,7 +7,9 @@ urlpatterns = patterns('', (r'^chartering/$', 'ietf.group.info.chartering_groups'), (r'^chartering/create/(?P(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/, + # need to unify these at some point (r'^(?P[a-zA-Z0-9-._]+)/$', 'ietf.group.info.group_home', None, "group_home"), (r'^(?P[a-zA-Z0-9-._]+)/documents/$', 'ietf.group.info.group_documents', None, "group_docs"), (r'^(?P[a-zA-Z0-9-._]+)/charter/$', 'ietf.group.info.group_about', None, 'group_charter'), @@ -25,10 +27,8 @@ urlpatterns = patterns('', (r'^(?P[a-zA-Z0-9-._]+)/about/(?P.)?$', 'ietf.group.info.group_about', None, 'group_about'), (r'^(?P[a-zA-Z0-9-._]+)/materials/$', 'ietf.group.info.materials', None, "group_materials"), - (r'^(?P[a-zA-Z0-9-._]+)/materials/new/$', 'ietf.group.edit.choose_material_type', None, "group_choose_material_type"), - (r'^(?P[a-zA-Z0-9-._]+)/materials/new/(?P[\w-]+)/$', 'ietf.group.edit.edit_material', { 'action': "new" }, "group_new_material"), - (r'^(?P[a-zA-Z0-9-._]+)/materials/(?P[^/]+)/edit/$', 'ietf.group.edit.edit_material', { 'action': "edit" }, "group_edit_material"), - (r'^(?P[a-zA-Z0-9-._]+)/materials/(?P[^/]+)/revise/$', 'ietf.group.edit.edit_material', { 'action': "revise" }, "group_revise_material"), + (r'^(?P[a-zA-Z0-9-._]+)/materials/new/$', 'ietf.doc.views_material.choose_material_type'), + (r'^(?P[a-zA-Z0-9-._]+)/materials/new/(?P[\w-]+)/$', 'ietf.doc.views_material.edit_material', { 'action': "new" }, "group_new_material"), ) diff --git a/ietf/templates/doc/document_material.html b/ietf/templates/doc/document_material.html index 4c4902126..e1d13b40c 100644 --- a/ietf/templates/doc/document_material.html +++ b/ietf/templates/doc/document_material.html @@ -22,14 +22,14 @@ Title: - {{ doc.title }} + {{ doc.title }} State: - {{ doc.get_state.name }} + {{ doc.get_state.name }} @@ -51,9 +51,7 @@ {% if not snapshot and can_manage_material %} - Change title/state - - Revise content + Upload New Revision {% endif %} diff --git a/ietf/templates/group/choose_material_type.html b/ietf/templates/doc/material/choose_material_type.html similarity index 100% rename from ietf/templates/group/choose_material_type.html rename to ietf/templates/doc/material/choose_material_type.html diff --git a/ietf/templates/group/edit_material.html b/ietf/templates/doc/material/edit_material.html similarity index 59% rename from ietf/templates/group/edit_material.html rename to ietf/templates/doc/material/edit_material.html index 934d56abb..b6cf6ab35 100644 --- a/ietf/templates/group/edit_material.html +++ b/ietf/templates/doc/material/edit_material.html @@ -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 %} -

Upload Material for Group {{ group.acronym }}

+

{% if action == "new" or action == "revise" %}Upload{% else %}Edit{% endif %} {{ document_type.name }} for Group {{ group.acronym }}

{% if action == "new" %}

@@ -20,19 +20,16 @@ form.upload-material .submit-row td { padding-top: 1em; text-align: right; } {{ group.acronym }}. The file will appear under the materials tab in the group pages.

-{% elif action == "edit" %} -

- Below you can edit the details of the material for the group - {{ group.acronym }}. -

+ +

Upload

{% elif action == "revise" %}

- 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 {{ group.acronym }}.

-{% endif %} -

Upload

+

Upload New Revision

+{% endif %}
{% csrf_token %} @@ -40,8 +37,8 @@ form.upload-material .submit-row td { padding-top: 1em; text-align: right; }
- Cancel - + Cancel +