From 114ba0ad13b894339eb6463f9e08786cdc3d6bc2 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Tue, 23 Feb 2016 04:00:16 +0000 Subject: [PATCH] winding up: added ability to add drafts to a session while looking at the session. - Legacy-Id: 10853 --- ietf/meeting/tests_views.py | 49 ++++++++++++++- ietf/meeting/urls.py | 1 + ietf/meeting/views.py | 48 ++++++++++++++- .../templates/meeting/add_session_drafts.html | 60 +++++++++++++++++++ ietf/templates/meeting/session_details.html | 7 ++- 5 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 ietf/templates/meeting/add_session_drafts.html diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index f5855ce99..9264c6ff8 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -3,6 +3,8 @@ import shutil import datetime import urlparse +import debug # pyflakes:ignore + from django.core.urlresolvers import reverse as urlreverse from django.conf import settings @@ -13,8 +15,10 @@ from ietf.meeting.models import Session, TimeSlot from ietf.meeting.test_data import make_meeting_test_data from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent +from ietf.person.factories import PersonFactory from ietf.group.factories import GroupFactory from ietf.meeting.factories import SessionFactory, SessionPresentationFactory +from ietf.doc.factories import DocumentFactory class MeetingTests(TestCase): def setUp(self): @@ -338,8 +342,51 @@ class SessionDetailsTests(TestCase): SessionPresentationFactory.create(session=session,document__type_id='slides') SessionPresentationFactory.create(session=session,document__type_id='agenda') - url = urlreverse("ietf.meeting.views.session_details", kwargs=dict(num=session.meeting.number, acronym=group.acronym)) + url = urlreverse('ietf.meeting.views.session_details', kwargs=dict(num=session.meeting.number, acronym=group.acronym)) r = self.client.get(url) self.assertTrue(all([x in unicontent(r) for x in ('slides','agenda','minutes','draft')])) self.assertFalse('deleted' in unicontent(r)) + def test_add_session_drafts(self): + group = GroupFactory.create(type_id='wg',state_id='active') + group_chair = PersonFactory.create() + group.role_set.create(name_id='chair',person = group_chair, email = group_chair.email()) + session = SessionFactory.create(meeting__type_id='ietf',group=group, meeting__date=datetime.date.today()+datetime.timedelta(days=90)) + SessionPresentationFactory.create(session=session,document__type_id='draft',rev=None) + old_draft = session.sessionpresentation_set.filter(document__type='draft').first().document + new_draft = DocumentFactory(type_id='draft') + + url = urlreverse('ietf.meeting.views.add_session_drafts', kwargs=dict(num=session.meeting.number, session_id=session.pk)) + + r = self.client.get(url) + self.assertEqual(r.status_code, 404) + + self.client.login(username="plain",password="plain+password") + r = self.client.get(url) + self.assertEqual(r.status_code, 404) + + self.client.login(username=group_chair.user.username, password='%s+password'%group_chair.user.username) + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + self.assertTrue(old_draft.name in unicontent(r)) + + r = self.client.post(url,dict(drafts=[new_draft.name,old_draft.name])) + self.assertTrue(r.status_code, 200) + q=PyQuery(r.content) + self.assertTrue(q('form .alert-danger:contains("Already linked:")')) + + self.assertEqual(1,session.sessionpresentation_set.count()) + r = self.client.post(url,dict(drafts=[new_draft.name,])) + self.assertTrue(r.status_code, 302) + self.assertEqual(2,session.sessionpresentation_set.count()) + + session.meeting.date -= datetime.timedelta(days=180) + session.meeting.save() + r = self.client.get(url) + self.assertEqual(r.status_code,404) + self.client.login(username='secretary',password='secretary+password') + r = self.client.get(url) + self.assertEqual(r.status_code,200) + q = PyQuery(r.content) + self.assertEqual(1,len(q(".alert-warning:contains('may affect published proceedings')"))) + diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py index 380b26241..9c8ef3af8 100644 --- a/ietf/meeting/urls.py +++ b/ietf/meeting/urls.py @@ -8,6 +8,7 @@ from ietf.meeting import ajax safe_for_all_meeting_types = [ url(r'^session/(?P[A-Za-z0-9_\-\+]+)/$', views.session_details), + url(r'^session/(?P\d+)/drafts$', views.add_session_drafts), ] type_ietf_only_patterns = [ diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 7f8296667..a67040835 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -13,7 +13,7 @@ import json import debug # pyflakes:ignore from django import forms -from django.shortcuts import render, redirect +from django.shortcuts import render, redirect, get_object_or_404 from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404 from django.contrib import messages from django.core.urlresolvers import reverse @@ -23,7 +23,7 @@ from django.forms.models import modelform_factory from django.forms import ModelForm from django.views.decorators.csrf import ensure_csrf_cookie -from ietf.doc.models import Document, State +from ietf.doc.models import Document, State, DocEvent from ietf.group.models import Group from ietf.group.utils import can_manage_materials from ietf.ietfauth.utils import role_required, has_role @@ -39,6 +39,8 @@ from ietf.meeting.helpers import convert_draft_to_pdf from ietf.utils.pipe import pipe from ietf.utils.pdf import pdf_pages +from ietf.doc.fields import SearchableDocumentsField + def materials(request, meeting_num=None): meeting = get_meeting(meeting_num) @@ -874,3 +876,45 @@ def session_details(request, num, acronym ): 'can_manage_materials' : can_manage, 'type_counter': type_counter, }) + +class SessionDraftsForm(forms.Form): + drafts = SearchableDocumentsField(required=False) + + def __init__(self, *args, **kwargs): + self.already_linked = kwargs.pop('already_linked') + super(self.__class__, self).__init__(*args, **kwargs) + + def clean(self): + selected = self.cleaned_data['drafts'] + problems = set(selected).intersection(set(self.already_linked)) + if problems: + raise forms.ValidationError("Already linked: %s" % ', '.join([d.name for d in problems])) + return self.cleaned_data + +def add_session_drafts(request, session_id, num): + # num is redundant, but we're dragging it along an artifact of where we are in the current URL structure + session = get_object_or_404(Session,pk=session_id) + if not session.can_manage_materials(request.user): + raise Http404 + if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"): + raise Http404 + + already_linked = [sp.document for sp in session.sessionpresentation_set.filter(document__type_id='draft')] + + if request.method == 'POST': + form = SessionDraftsForm(request.POST,already_linked=already_linked) + if form.is_valid(): + for draft in form.cleaned_data['drafts']: + session.sessionpresentation_set.create(document=draft,rev=None) + c = DocEvent(type="added_comment", doc=draft, by=request.user.person) + c.desc = "Added to session: %s" % session + c.save() + return redirect('ietf.meeting.views.session_details', num=session.meeting.number, acronym=session.group.acronym) + else: + form = SessionDraftsForm(already_linked=already_linked) + + return render(request, "meeting/add_session_drafts.html", + { 'session': session, + 'already_linked': session.sessionpresentation_set.filter(document__type_id='draft'), + 'form': form, + }) diff --git a/ietf/templates/meeting/add_session_drafts.html b/ietf/templates/meeting/add_session_drafts.html new file mode 100644 index 000000000..3fa968f00 --- /dev/null +++ b/ietf/templates/meeting/add_session_drafts.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2015, All Rights Reserved #} +{% load origin staticfiles bootstrap3 %} + +{% block title %}Add drafts to {{ session.meeting }} : {{ session.group.acronym }}{% endblock %} + +{% block pagehead %} + + +{% endblock %} + +{% block content %} + {% origin %} + +

Add drafts to {{ session.meeting }} : {{ session.group.acronym }}

+ {% comment %} TODO: put the session name here or calculate the number at the meeting {% endcomment %} + + {% if session.is_material_submission_cutoff %} +
The deadline for submission corrections has passed. This may affect published proceedings.
+ {% endif %} + +
This form will link additional drafts to this session with a revision of "Current at time of presentation". For more fine grained control of versions, or to remove a draft from a session, adjust the sessions associated with a draft from the draft's main page.
+
+
Drafts already linked to this sesssion
+
+ + + + + + {% for sp in already_linked %} + + + + + {% endfor %} +
RevisionDocument
{% if sp.rev %}-{{sp.rev}}{% else %}(current){% endif %}{{sp.document.title}} ({{sp.document.name}})
+
+
+ +
+
Additional drafts to link to this session
+
+
+ {% csrf_token %} + {% bootstrap_form form %} + {% buttons %} + + Cancel + {% endbuttons %} +
+
+
+ +{% endblock %} + +{% block js %} + + +{% endblock %} diff --git a/ietf/templates/meeting/session_details.html b/ietf/templates/meeting/session_details.html index fc012d784..c67e7e554 100644 --- a/ietf/templates/meeting/session_details.html +++ b/ietf/templates/meeting/session_details.html @@ -15,7 +15,10 @@ {% if session.status.slug == 'sched' or session.status.slug == 'schedw' %}
- Upload/Edit Materials + Upload/Edit materials + + + Link additional drafts to session {% if not type_counter.agenda %} This session does not yet have an agenda @@ -38,7 +41,7 @@ {% else %} {% url 'doc_view' name=pres.document.name as url %} {% endif %} - {{pres.document.title}} ({{ pres.document.name }}-{{ pres.rev }}) + {{pres.document.title}} ({{ pres.document.name }}{% if pres.rev %}-{{ pres.rev }}{% endif %})