View to design/remove delegates into an stream. Fixes #699
- Legacy-Id: 3300
This commit is contained in:
parent
322caf332c
commit
b28469f117
|
@ -1,5 +1,3 @@
|
|||
from django.db.models.query import QuerySet
|
||||
|
||||
from ietf.ietfworkflows.streams import get_streamed_draft
|
||||
|
||||
|
||||
|
@ -24,20 +22,42 @@ def is_wgdelegate(person):
|
|||
return bool(person.wgdelegate_set.all())
|
||||
|
||||
|
||||
def is_chair_of_draft(user, draft):
|
||||
def is_delegate_of_stream(user, stream):
|
||||
if is_secretariat(user):
|
||||
return True
|
||||
person = get_person_for_user(user)
|
||||
return stream.check_delegate(person)
|
||||
|
||||
|
||||
def is_chair_of_stream(user, stream):
|
||||
if is_secretariat(user):
|
||||
return True
|
||||
person = get_person_for_user(user)
|
||||
return stream.check_chair(person)
|
||||
|
||||
|
||||
def is_authorized_in_draft_stream(user, draft):
|
||||
if is_secretariat(user):
|
||||
return True
|
||||
person = get_person_for_user(user)
|
||||
if not person:
|
||||
return False
|
||||
streamed = get_streamed_draft(draft)
|
||||
if not streamed or not streamed.stream:
|
||||
return False
|
||||
# Check if the person is chair of the stream
|
||||
if is_chair_of_stream(user, streamed.stream):
|
||||
return True
|
||||
# Check if the person is delegate of the stream
|
||||
if is_delegate_of_stream(user, streamed.stream):
|
||||
return True
|
||||
# Check if the person is chair of the related group
|
||||
chairs = streamed.stream.get_chairs_for_document(draft)
|
||||
if not chairs:
|
||||
return False
|
||||
if isinstance(chairs, QuerySet):
|
||||
return bool(chairs.filter(person=person).count())
|
||||
else:
|
||||
return person in chairs
|
||||
if chairs and person in chairs:
|
||||
return True
|
||||
# Check if the person is authorized by a delegate system
|
||||
delegates = streamed.stream.get_delegates_for_document(draft)
|
||||
return bool(person in delegates)
|
||||
|
||||
|
||||
def can_edit_state(user, draft):
|
||||
|
@ -50,7 +70,7 @@ def can_edit_state(user, draft):
|
|||
is_wgchair(person) or
|
||||
is_wgdelegate(person))
|
||||
return (is_secretariat(user) or
|
||||
is_chair_of_draft(user, draft))
|
||||
is_authorized_in_draft_stream(user, draft))
|
||||
|
||||
|
||||
def can_edit_stream(user, draft):
|
||||
|
|
|
@ -8,7 +8,7 @@ from workflows.utils import set_workflow_for_object
|
|||
|
||||
from ietf.idtracker.models import PersonOrOrgInfo, IETFWG
|
||||
from ietf.wgchairs.accounts import get_person_for_user
|
||||
from ietf.ietfworkflows.models import Stream
|
||||
from ietf.ietfworkflows.models import Stream, StreamDelegate
|
||||
from ietf.ietfworkflows.utils import (get_workflow_for_draft, get_workflow_for_wg,
|
||||
get_state_for_draft, get_state_by_name,
|
||||
update_state, FOLLOWUP_TAG,
|
||||
|
@ -62,7 +62,7 @@ class NoWorkflowStateForm(StreamDraftForm):
|
|||
wgs = set([i.group_acronym for i in self.person.wgchair_set.all()]).union(set([i.wg for i in self.person.wgdelegate_set.all()]))
|
||||
if len(wgs) > 1:
|
||||
self.wgs = list(wgs)
|
||||
self.wgs.sort(lambda x,y: cmp(x.group_acronym.acronym, y.group_acronym.acronym))
|
||||
self.wgs.sort(lambda x, y: cmp(x.group_acronym.acronym, y.group_acronym.acronym))
|
||||
self.fields['wg'].choices = [(i.pk, '%s - %s' % (i.group_acronym.acronym, i.group_acronym.name)) for i in self.wgs]
|
||||
else:
|
||||
self.onlywg = list(wgs)[0].group_acronym
|
||||
|
@ -200,3 +200,28 @@ class DraftStreamForm(StreamDraftForm):
|
|||
comment=comment,
|
||||
person=self.person,
|
||||
to_stream=to_stream)
|
||||
|
||||
|
||||
class StreamDelegatesForm(forms.Form):
|
||||
email = forms.EmailField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.stream = kwargs.pop('stream')
|
||||
super(StreamDelegatesForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_person(self, email):
|
||||
persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).distinct()
|
||||
if not persons:
|
||||
return None
|
||||
return persons[0]
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data.get('email')
|
||||
self.person = self.get_person(email)
|
||||
if not self.person:
|
||||
raise forms.ValidationError('There is no user with this email in the system')
|
||||
|
||||
def save(self):
|
||||
StreamDelegate.objects.get_or_create(
|
||||
person=self.person,
|
||||
stream=self.stream)
|
||||
|
|
|
@ -148,7 +148,7 @@ class Stream(models.Model):
|
|||
|
||||
def _irtf_group(self, document):
|
||||
filename = document.filename.split('-')
|
||||
if len(filename) > 2 and filename[0] == 'draft' and filename[1] =='irtf':
|
||||
if len(filename) > 2 and filename[0] == 'draft' and filename[1] == 'irtf':
|
||||
try:
|
||||
return IRTF.objects.get(acronym=filename[2])
|
||||
except IRTF.DoesNotExist:
|
||||
|
@ -163,7 +163,6 @@ class Stream(models.Model):
|
|||
chairs.append(Role.objects.get(pk=Role.IRTF_CHAIR).person)
|
||||
return chairs
|
||||
|
||||
|
||||
def _ietf_delegates_for_document(self, document):
|
||||
group = self.get_group_for_document(document)
|
||||
if not group:
|
||||
|
@ -215,8 +214,8 @@ class Stream(models.Model):
|
|||
def get_chairs(self):
|
||||
chairs = []
|
||||
if hasattr(self, '_%s_stream_chairs' % self.name.lower()):
|
||||
chairs += list(getattr(self, '_%s_stream_chairs' % self.name.lower())(person))
|
||||
|
||||
chairs += list(getattr(self, '_%s_stream_chairs' % self.name.lower())())
|
||||
|
||||
role_key = getattr(Role, '%s_CHAIR' % self.name.upper(), None)
|
||||
if role_key:
|
||||
try:
|
||||
|
@ -228,7 +227,7 @@ class Stream(models.Model):
|
|||
def get_delegates(self):
|
||||
delegates = []
|
||||
if hasattr(self, '_%s_stream_delegates' % self.name.lower()):
|
||||
delegates += list(getattr(self, '_%s_stream_delegates' % self.name.lower())(person))
|
||||
delegates += list(getattr(self, '_%s_stream_delegates' % self.name.lower())())
|
||||
delegates += [i.person for i in StreamDelegate.objects.filter(stream=self)]
|
||||
return list(set(delegates))
|
||||
|
||||
|
|
|
@ -6,7 +6,9 @@ from ietf.ietfworkflows.utils import (get_workflow_for_draft,
|
|||
from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document,
|
||||
can_manage_writeup_of_a_document)
|
||||
from ietf.ietfworkflows.streams import get_stream_from_wrapper
|
||||
from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream)
|
||||
from ietf.ietfworkflows.models import Stream
|
||||
from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream,
|
||||
is_chair_of_stream)
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
@ -14,7 +16,6 @@ register = template.Library()
|
|||
|
||||
@register.inclusion_tag('ietfworkflows/stream_state.html', takes_context=True)
|
||||
def stream_state(context, doc):
|
||||
request = context.get('request', None)
|
||||
data = {}
|
||||
stream = get_stream_from_wrapper(doc)
|
||||
data.update({'stream': stream})
|
||||
|
@ -72,3 +73,33 @@ def edit_actions(context, wrapper):
|
|||
'draft': draft,
|
||||
'doc': wrapper,
|
||||
}
|
||||
|
||||
|
||||
class StreamListNode(template.Node):
|
||||
|
||||
def __init__(self, user, var_name):
|
||||
self.user = user
|
||||
self.var_name = var_name
|
||||
|
||||
def render(self, context):
|
||||
user = self.user.resolve(context)
|
||||
streams = []
|
||||
for i in Stream.objects.all():
|
||||
if is_chair_of_stream(user, i):
|
||||
streams.append(i)
|
||||
context.update({self.var_name: streams})
|
||||
return ''
|
||||
|
||||
|
||||
@register.tag
|
||||
def get_user_managed_streams(parser, token):
|
||||
firstbits = token.contents.split(None, 2)
|
||||
if len(firstbits) != 3:
|
||||
raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments")
|
||||
user = parser.compile_filter(firstbits[1])
|
||||
lastbits_reversed = firstbits[2][::-1].split(None, 2)
|
||||
if lastbits_reversed[1][::-1] != 'as':
|
||||
raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must"
|
||||
" be 'as'")
|
||||
var_name = lastbits_reversed[0][::-1]
|
||||
return StreamListNode(user, var_name)
|
||||
|
|
|
@ -6,4 +6,5 @@ urlpatterns = patterns('ietf.ietfworkflows.views',
|
|||
url(r'^(?P<name>[^/]+)/history/$', 'stream_history', name='stream_history'),
|
||||
url(r'^(?P<name>[^/]+)/edit/state/$', 'edit_state', name='edit_state'),
|
||||
url(r'^(?P<name>[^/]+)/edit/stream/$', 'edit_stream', name='edit_stream'),
|
||||
url(r'^delegates/(?P<stream_name>[^/]+)/$', 'stream_delegates', name='stream_delegates'),
|
||||
)
|
||||
|
|
|
@ -3,16 +3,19 @@ from django.shortcuts import get_object_or_404, render_to_response
|
|||
from django.template import RequestContext
|
||||
|
||||
from ietf.idtracker.models import InternetDraft
|
||||
from ietf.ietfworkflows.models import Stream, StreamDelegate
|
||||
from ietf.ietfworkflows.forms import (DraftTagsStateForm,
|
||||
DraftStreamForm,
|
||||
NoWorkflowStateForm)
|
||||
NoWorkflowStateForm,
|
||||
StreamDelegatesForm)
|
||||
from ietf.ietfworkflows.streams import (get_stream_from_draft,
|
||||
get_streamed_draft)
|
||||
from ietf.ietfworkflows.utils import (get_workflow_history_for_draft,
|
||||
get_workflow_for_draft,
|
||||
get_annotation_tags_for_draft,
|
||||
get_state_for_draft)
|
||||
from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream)
|
||||
from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream,
|
||||
is_chair_of_stream)
|
||||
|
||||
|
||||
REDUCED_HISTORY_LEN = 20
|
||||
|
@ -81,8 +84,34 @@ def edit_state(request, name):
|
|||
return HttpResponseForbidden('You have no permission to access this view')
|
||||
return _edit_draft_stream(request, draft, DraftTagsStateForm)
|
||||
|
||||
|
||||
def edit_stream(request, name):
|
||||
draft = get_object_or_404(InternetDraft, filename=name)
|
||||
if not can_edit_stream(request.user, draft):
|
||||
return HttpResponseForbidden('You have no permission to access this view')
|
||||
return _edit_draft_stream(request, draft, DraftStreamForm)
|
||||
|
||||
|
||||
def stream_delegates(request, stream_name):
|
||||
stream = get_object_or_404(Stream, name=stream_name)
|
||||
if not is_chair_of_stream(request.user, stream):
|
||||
return HttpResponseForbidden('You have no permission to access this view')
|
||||
chairs = stream.get_chairs()
|
||||
form = StreamDelegatesForm(stream=stream)
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('delete', False):
|
||||
pk_list = request.POST.getlist('remove_delegate')
|
||||
StreamDelegate.objects.filter(stream=stream, person__pk__in=pk_list).delete()
|
||||
else:
|
||||
form = StreamDelegatesForm(request.POST, stream=stream)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
form = StreamDelegatesForm(stream=stream)
|
||||
delegates = stream.get_delegates()
|
||||
return render_to_response('ietfworkflows/stream_delegates.html',
|
||||
{'stream': stream,
|
||||
'chairs': chairs,
|
||||
'delegates': delegates,
|
||||
'form': form,
|
||||
},
|
||||
context_instance=RequestContext(request))
|
||||
|
|
|
@ -33,7 +33,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
{% endcomment %}
|
||||
{% load wg_menu %}
|
||||
{% load ietf_filters %}
|
||||
{% load ietf_filters ietf_streams %}
|
||||
<ul>
|
||||
{% if user|in_group:"Area_Director" %}
|
||||
<li class="sect first">AD Dashboard</li>
|
||||
|
@ -47,6 +47,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
<li><a href="{% url ietf.iesg.views.telechat_dates %}">Telechat Dates</a></li>
|
||||
<li><a href="{% url iesg_working_group_actions %}">Working Groups</a></li>
|
||||
{% endif %}
|
||||
{% get_user_managed_streams user as stream_list %}
|
||||
{% if stream_list %}
|
||||
<li class="sect{% if not user|in_group:"Area_Director,Secretariat" %} first{% endif %}">Streams</li>
|
||||
{% for stream in stream_list %}
|
||||
<li{% if forloop.last %} style="margin-bottom: 2px;"{% endif %}><a href="{% url stream_delegates stream.name %}">{{ stream.name }} stream</a></li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<li class="sect{% if not user|in_group:"Area_Director,Secretariat" %} first{% endif %}">Working Groups</li>
|
||||
|
||||
<li style="padding-bottom:0;"><div id="wgs" class="yuimenu"><div class="bd" style="border:0;">
|
||||
|
|
41
ietf/templates/ietfworkflows/stream_delegates.html
Normal file
41
ietf/templates/ietfworkflows/stream_delegates.html
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% extends "base.html" %}
|
||||
{% load ietf_streams ietf_filters %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Stream: {{ stream.name }}</h1>
|
||||
|
||||
{% if chairs %}
|
||||
<h2>{{ stream.name }} stream chairs</h2>
|
||||
<table class="ietf-table">
|
||||
<tr><th>Name</th><th>Email</th></tr>
|
||||
{% for chair in chairs %}
|
||||
<tr class="{% cycle "oddrow" "evenrow" %}"><td>{{ chair }}</td><td>{{ chair.email.0 }} <{{ chair.email.1 }}></td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<h2>{{ stream.name }} stream delegates</h2>
|
||||
<form action="" method="POST">
|
||||
{% if delegates %}
|
||||
<table class="ietf-table">
|
||||
<tr><th></th><th>Name</th><th>Email</th></tr>
|
||||
{% for delegate in delegates %}
|
||||
<tr class="{% cycle "oddrow" "evenrow" %}"><td><input type="checkbox" name="remove_delegate" value="{{ delegate.pk }}" /></td><td>{{ delegate }}</td><td>{{ delegate.email.0 }} <{{ delegate.email.1 }}></td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p>
|
||||
<input type="submit" value="Remove selected delegates" name="delete" />
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
No delegates for {{ stream.name }} stream asigned.
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
Enter a valid e-mail address to add a new delegate.
|
||||
</p>
|
||||
{% if form.errors.email %}{{ form.errors.email }}{% endif %}
|
||||
{{ form.email }}
|
||||
<input type="submit" value="Add new delegate" />
|
||||
</form>
|
||||
{% endblock %}
|
Loading…
Reference in a new issue