Add the ability to add a comment to a group's history. Fixes #1483. Commit ready for merge.

- Legacy-Id: 14468
This commit is contained in:
Robert Sparks 2017-12-28 20:48:58 +00:00
parent 0a9737a5d2
commit 13e8f8982c
10 changed files with 10177 additions and 10021 deletions

View file

@ -57,3 +57,12 @@ def email_milestones_changed(request, group, changes, states):
if (addrs.to or addrs.cc) and msg:
wrap_up_email(addrs, msg)
def email_comment(request, event):
(to, cc) = gather_address_lists('group_added_comment',group=event.group)
send_mail(request, to, None, "Comment added to %s history"%event.group.acronym,
"group/comment_added_email.txt",
dict( event = event,
group_url=settings.IDTRACKER_BASE_URL + event.group.about_url(),
),
cc = cc)

View file

@ -738,6 +738,23 @@ class GroupEditTests(TestCase):
group = Group.objects.get(acronym=group.acronym)
self.assertEqual(group.state_id, "active")
def test_add_comment(self):
make_test_data()
group = Group.objects.get(acronym="mars")
url = urlreverse('ietf.group.views.add_comment', kwargs=dict(acronym=group.acronym))
empty_outbox()
for username in ['secretary','ad','marschairman','marssecretary','marsdelegate']:
login_testing_unauthorized(self, username, url)
r = self.client.post(url, dict(comment="Test comment %s"%username))
self.assertEqual(r.status_code, 302)
person = Person.objects.get(user__username=username)
self.assertTrue(GroupEvent.objects.filter(group=group,by=person,type='added_comment',desc='Test comment %s'%username).exists())
self.client.logout()
self.client.login(username='ameschairman',password='ameschairman+password')
r=self.client.get(url)
self.assertEqual(r.status_code,403)
self.assertEqual(len(outbox),5)
class MilestoneTests(TestCase):
def create_test_milestones(self):
draft = make_test_data()

View file

@ -24,6 +24,7 @@ info_detail_urls = [
url(r'^about/status/edit/$', views.group_about_status_edit),
url(r'^about/status/meeting/(?P<num>\d+)/$', views.group_about_status_meeting),
url(r'^history/$',views.history),
url(r'^history/addcomment/$',views.add_comment),
url(r'^email/$', views.email),
url(r'^deps/(?P<output_type>[\w-]+)/$', views.dependencies),
url(r'^meetings/$', views.meetings),

View file

@ -41,6 +41,7 @@ import datetime
from tempfile import mkstemp
from collections import OrderedDict, defaultdict
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.db.models.aggregates import Max
@ -65,7 +66,7 @@ from ietf.group.dot import make_dot
from ietf.group.forms import (GroupForm, StatusUpdateForm, ConcludeGroupForm, StreamEditForm,
ManageReviewRequestForm, EmailOpenAssignmentsForm, ReviewerSettingsForm,
AddUnavailablePeriodForm, EndUnavailablePeriodForm, ReviewSecretarySettingsForm, )
from ietf.group.mails import email_admin_re_charter, email_personnel_change
from ietf.group.mails import email_admin_re_charter, email_personnel_change, email_comment
from ietf.group.models import ( Group, Role, GroupEvent, GroupStateTransitions, GroupURL, ChangeStateGroupEvent )
from ietf.group.utils import (get_charter_text, can_manage_group_type,
milestone_reviewer_for_group_type, can_provide_status_update,
@ -74,7 +75,7 @@ from ietf.group.utils import (get_charter_text, can_manage_group_type,
save_group_in_history, can_manage_group,
get_group_or_404, setup_default_community_list_for_group, )
#
from ietf.ietfauth.utils import has_role
from ietf.ietfauth.utils import has_role, is_authorized_in_group
from ietf.mailtrigger.utils import gather_relevant_expansions
from ietf.meeting.helpers import get_meeting
from ietf.meeting.utils import group_sessions
@ -632,10 +633,13 @@ def history(request, acronym, group_type=None):
group = get_group_or_404(acronym, group_type)
events = group.groupevent_set.all().select_related('by').order_by('-time', '-id')
can_add_comment = is_authorized_in_group(request.user,group)
return render(request, 'group/history.html',
construct_group_menu_context(request, group, "history", group_type, {
"group": group,
"events": events,
"can_add_comment": can_add_comment,
}))
def materials(request, acronym, group_type=None):
@ -1784,4 +1788,24 @@ def change_review_secretary_settings(request, acronym, group_type=None):
'back_url': back_url,
'settings_form': settings_form,
})
class AddCommentForm(forms.Form):
comment = forms.CharField(required=True, widget=forms.Textarea, strip=False)
def add_comment(request, acronym, group_type=None):
group = get_group_or_404(acronym, group_type)
if not is_authorized_in_group(request.user,group):
return HttpResponseForbidden("You need to a chair, secretary, or delegate of this group to add a comment.")
if request.method == 'POST':
form = AddCommentForm(request.POST)
if form.is_valid():
comment = form.cleaned_data['comment']
event = GroupEvent.objects.create(group=group,desc=comment,type="added_comment",by=request.user.person)
email_comment(request,event)
return redirect('ietf.group.views.history', acronym=group.acronym)
else:
form = AddCommentForm()
return render(request, 'group/add_comment.html', { 'group':group, 'form':form, })

View file

@ -144,6 +144,26 @@ def is_authorized_in_doc_stream(user, doc):
return Role.objects.filter(Q(name__in=("chair", "secr", "delegate", "auth"), person__user=user) & group_req).exists()
def is_authorized_in_group(user, group):
"""Return whether user is authorized to perform duties on
a given group."""
if not user.is_authenticated:
return False
if has_role(user, ["Secretariat",]):
return True
if group.parent:
if group.parent.type_id == 'area' and has_role(user, ['Area Director',]):
return True
if group.parent.acronym == 'irtf' and has_role(user, ['IRTF Chair',]):
return True
if group.parent.acronym == 'iab' and has_role(user, ['IAB','IAB Executive Director',]):
return True
return Role.objects.filter(name__in=("chair", "secr", "delegate", "auth"), person__user=user,group=group ).exists()
def is_individual_draft_author(user, doc):
if not user.is_authenticated:

View file

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2017-12-28 11:11
from __future__ import unicode_literals
from django.db import migrations
def forward(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger','MailTrigger')
Recipient = apps.get_model('mailtrigger', 'Recipient')
group_added_comment = MailTrigger.objects.create(
slug='group_added_comment',
desc="Recipients when a comment is added to a group's history",
)
group_added_comment.to = Recipient.objects.filter(slug__in=[
'group_chairs',
'group_secretaries',
'group_responsible_directors',
])
def reverse(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger','MailTrigger')
MailTrigger.objects.filter(slug='group_added_comment').delete()
class Migration(migrations.Migration):
dependencies = [
('mailtrigger', '0010_auto_20161207_1104'),
]
operations = [
migrations.RunPython(forward, reverse)
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load bootstrap3 %}
{% block title %}Add comment for {{ group }}{% endblock %}
{% block content %}
{% origin %}
<h1>Add comment<br><small>{{ group }} ({{ group.acronym }})</small></h1>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<p class="help-block">The comment will be added to the history trail for the group.</p>
{% buttons %}
<button type="submit" class="btn btn-primary">Submit</button>
<a class="btn btn-default pull-right" href="{% url "ietf.group.views.history" acronym=group.acronym %}">Back</a>
{% endbuttons %}
</form>
{% endblock %}

View file

@ -0,0 +1,10 @@
{% autoescape off %}
Please DO NOT reply to this email.
{{event.by}} added the following comment to the history of {{event.group.acronym}}:
{{ event.desc }}
Information for the group can be found at {{ group_url }}.
{% endautoescape%}

View file

@ -10,6 +10,10 @@
{% block group_content %}
{% origin %}
<h2>Group History</h2>
{% if can_add_comment %}
<a class="btn btn-default" href="{% url 'ietf.group.views.add_comment' acronym=group.acronym %}"><span class="fa fa-plus"></span> Add comment</a>
{% endif %}
<table class="table table-condensed table-striped tablesorter">
<thead>
<tr>
@ -35,4 +39,4 @@
{% block js %}
<script src="{% static "jquery.tablesorter/js/jquery.tablesorter.combined.min.js" %}"></script>
{% endblock %}
{% endblock %}