From 34cbc464cb2fedd4996ae4014d61816ca712d8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20A=2E=20S=C3=A1nchez=20L=C3=B3pez?= Date: Mon, 29 Nov 2010 09:15:52 +0000 Subject: [PATCH] Manage delegates views. See #557 - Legacy-Id: 2690 --- ietf/templates/wgchairs/manage_delegates.html | 55 ++++++ ietf/templates/wgchairs/notexistdelegate.html | 12 ++ .../wgchairs/wgchairs_admin_options.html | 15 ++ ietf/templates/wginfo/wg_base.html | 3 +- ietf/wgchairs/accounts.py | 2 +- ietf/wgchairs/forms.py | 179 ++++++++++++++++++ ietf/wgchairs/templatetags/__init__.py | 0 ietf/wgchairs/templatetags/wgchairs_tags.py | 19 ++ ietf/wgchairs/urls.py | 8 + ietf/wgchairs/views.py | 38 ++++ 10 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 ietf/templates/wgchairs/manage_delegates.html create mode 100644 ietf/templates/wgchairs/notexistdelegate.html create mode 100644 ietf/templates/wgchairs/wgchairs_admin_options.html create mode 100644 ietf/wgchairs/forms.py create mode 100644 ietf/wgchairs/templatetags/__init__.py create mode 100644 ietf/wgchairs/templatetags/wgchairs_tags.py create mode 100644 ietf/wgchairs/urls.py create mode 100644 ietf/wgchairs/views.py diff --git a/ietf/templates/wgchairs/manage_delegates.html b/ietf/templates/wgchairs/manage_delegates.html new file mode 100644 index 000000000..8afd102f0 --- /dev/null +++ b/ietf/templates/wgchairs/manage_delegates.html @@ -0,0 +1,55 @@ +{% extends "wginfo/wg_base.html" %} + +{% block morecss %} +{{ block.super }} +.wg-chair-management ul { + list-style: none; +} + +.wg-chair-management input { + border: 1px solid green; +} +{% endblock %} + +{% block wg_titledetail %}Delegates{% endblock %} + +{% block wg_content %} +
+

Add new delegate

+{% if add_form.message %} +
+ {{ add_form.message.value }} +
+{% endif %} +{% if can_add %} +
+ {{ add_form.as_p }} +

+ + {% if add_form.can_cancel %}No! I don't want to continue{% endif %} +

+
+{% else %} +

+You can only assign three delegates. Please remove delegates to add a new one. +

+{% endif %} + +

Delegates

+{% if delegates %} +
+ + + {% for delegate in delegates %} + + {% endfor %} +
RemoveDelegate name
{{ delegate.person }}
+ +
+{% else %} +

+No delegates +

+{% endif %} +
+{% endblock %} diff --git a/ietf/templates/wgchairs/notexistdelegate.html b/ietf/templates/wgchairs/notexistdelegate.html new file mode 100644 index 000000000..3a7fa6482 --- /dev/null +++ b/ietf/templates/wgchairs/notexistdelegate.html @@ -0,0 +1,12 @@ +

+The delegate you are trying to designate does not have a personal user-id and password to log-on to the Datatracker. +

+

+An email will be sent to the following address to inform that the person designated sould contact with the Secretariat +to obtain their own user-id and password to the Datatracker. +

+ diff --git a/ietf/templates/wgchairs/wgchairs_admin_options.html b/ietf/templates/wgchairs/wgchairs_admin_options.html new file mode 100644 index 000000000..5a8501839 --- /dev/null +++ b/ietf/templates/wgchairs/wgchairs_admin_options.html @@ -0,0 +1,15 @@ +{% if can_manage_workflow %} + {% ifequal selected "manage_workflow" %} + Manage workflow + {% else %} + Manage workflow + {% endifequal %} | +{% endif %} + +{% if can_manage_delegates %} + {% ifequal selected "manage_delegates" %} + Manage delegates + {% else %} + Manage delegates + {% endifequal %} | +{% endif %} diff --git a/ietf/templates/wginfo/wg_base.html b/ietf/templates/wginfo/wg_base.html index 438724c11..853c3368e 100644 --- a/ietf/templates/wginfo/wg_base.html +++ b/ietf/templates/wginfo/wg_base.html @@ -32,7 +32,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% endcomment %} -{% load ietf_filters %} +{% load ietf_filters wgchairs_tags %} {% block title %}{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}) - {% block wg_titledetail %}{% endblock %}{% endblock %} {% block morecss %} @@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% ifequal selected "documents" %}Documents{% else %}Documents{% endifequal %} | {% ifequal selected "charter" %}Charter{% else %}Charter{% endifequal %} | +{% wgchairs_admin_options wg %} {% if wg.clean_email_archive|startswith:"http:" or wg.clean_email_archive|startswith:"ftp:" %} List Archive » | {% endif %} diff --git a/ietf/wgchairs/accounts.py b/ietf/wgchairs/accounts.py index 4c519a288..95f45d147 100644 --- a/ietf/wgchairs/accounts.py +++ b/ietf/wgchairs/accounts.py @@ -22,7 +22,7 @@ def can_do_wg_workflow_in_document(user, document): person = get_person_for_user(user) if not person or not document.group: return False - return can_do_wg_wgorkflow_in_group(document.group) + return can_do_wg_workflow_in_group(document.group) def can_manage_workflow_in_group(user, group): diff --git a/ietf/wgchairs/forms.py b/ietf/wgchairs/forms.py new file mode 100644 index 000000000..39903e73b --- /dev/null +++ b/ietf/wgchairs/forms.py @@ -0,0 +1,179 @@ +from django import forms +from django.conf import settings +from django.core.mail import EmailMessage +from django.template.loader import render_to_string + +from ietf.wgchairs.models import WGDelegate +from ietf.wgchairs.accounts import get_person_for_user +from ietf.idtracker.models import PersonOrOrgInfo + + +class RelatedWGForm(forms.Form): + + can_cancel = False + + def __init__(self, *args, **kwargs): + self.wg = kwargs.pop('wg', None) + self.user = kwargs.pop('user', None) + self.message = {} + super(RelatedWGForm, self).__init__(*args, **kwargs) + + def get_message(self): + return self.message + + def set_message(self, msg_type, msg_value): + self.message = {'type': msg_type, + 'value': msg_value, + } + + +class RemoveDelegateForm(RelatedWGForm): + + delete = forms.MultipleChoiceField() + + def __init__(self, *args, **kwargs): + super(RemoveDelegateForm, self).__init__(*args, **kwargs) + self.fields['delete'].choices = [(i.pk, i.pk) for i in self.wg.wgdelegate_set.all()] + + def save(self): + delegates = self.cleaned_data.get('delete') + WGDelegate.objects.filter(pk__in=delegates).delete() + self.set_message('success', 'Delegates removed') + + +class AddDelegateForm(RelatedWGForm): + + email = forms.EmailField() + form_type = forms.CharField(widget=forms.HiddenInput, initial='single') + + def __init__(self, *args, **kwargs): + super(AddDelegateForm, self).__init__(*args, **kwargs) + self.next_form = self + + def get_next_form(self): + return self.next_form + + def get_person(self, email): + persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email, iesglogin__isnull=False).distinct() + if not persons: + raise PersonOrOrgInfo.DoesNotExist + if len(persons) > 1: + raise PersonOrOrgInfo.MultipleObjectsReturned + return persons[0] + + def save(self): + email = self.cleaned_data.get('email') + try: + person = self.get_person(email) + except PersonOrOrgInfo.DoesNotExist: + self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email) + self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system') + return + except PersonOrOrgInfo.MultipleObjectsReturned: + self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email) + self.next_form.set_message('multiple', 'There are multiple users with this email in the system') + return + self.create_delegate(person) + + def create_delegate(self, person): + (delegate, created) = WGDelegate.objects.get_or_create(wg=self.wg, + person=person) + if not created: + self.set_message('error', 'The email belongs to a person who is already a delegate') + else: + self.next_form = AddDelegateForm(wg=self.wg, user=self.user) + self.next_form.set_message('success', 'A new delegate has been added') + + +class MultipleDelegateForm(AddDelegateForm): + + email = forms.EmailField(widget=forms.HiddenInput) + form_type = forms.CharField(widget=forms.HiddenInput, initial='multiple') + persons = forms.ChoiceField(widget=forms.RadioSelect, help_text='Please select one person from the list') + submit_msg = 'Designate as delegate' + + def __init__(self, *args, **kwargs): + self.email = kwargs.pop('email', None) + super(MultipleDelegateForm, self).__init__(*args, **kwargs) + if not self.email: + self.email = self.data.get('email', None) + self.fields['email'].initial = self.email + self.fields['persons'].choices = [(i.pk, unicode(i)) for i in PersonOrOrgInfo.objects.filter(emailaddress__address=self.email, iesglogin__isnull=False).distinct().order_by('first_name')] + + def save(self): + person_id = self.cleaned_data.get('persons') + person = PersonOrOrgInfo.objects.get(pk=person_id) + self.create_delegate(person) + + +class NotExistDelegateForm(MultipleDelegateForm): + + email = forms.EmailField(widget=forms.HiddenInput) + form_type = forms.CharField(widget=forms.HiddenInput, initial='notexist') + can_cancel = True + submit_msg = 'Send email to these addresses' + + def __init__(self, *args, **kwargs): + super(NotExistDelegateForm, self).__init__(*args, **kwargs) + self.email_list = [] + + def get_email_list(self): + if self.email_list: + return self.email_list + email_list = [self.email] + email_list.append('IETF Secretariat ') + email_list += ['%s <%s>' % i.person.email() for i in self.wg.wgchair_set.all() if i.person.email()] + self.email_list = email_list + return email_list + + def as_p(self): + email_list = self.get_email_list() + return render_to_string('wgchairs/notexistdelegate.html', {'email_list': email_list}) + + def send_email(self, email, template): + subject = 'WG Delegate needs system credentials' + persons = PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).distinct() + body = render_to_string(template, + {'chair': get_person_for_user(self.user), + 'delegate_email': self.email, + 'delegate_persons': persons, + }) + mail = EmailMessage(subject=subject, + body=body, + to=email, + from_email=settings.DEFAULT_FROM_EMAIL) + return mail + + def send_email_to_delegate(self, email): + self.send_email(email, 'wgchairs/notexistsdelegate_delegate_email.txt') + + def send_email_to_secretariat(self, email): + self.send_email(email, 'wgchairs/notexistsdelegate_secretariat_email.txt') + + def send_email_to_wgchairs(self, email): + self.send_email(email, 'wgchairs/notexistsdelegate_wgchairs_email.txt') + + def save(self): + self.next_form = AddDelegateForm(wg=self.wg, user=self.user) + if settings.DEBUG: + self.next_form.set_message('warning', 'Email was not sent cause tool is in DEBUG mode') + else: + email_list = self.get_email_list() + self.send_email_to_delegate([email_list[0]]) + self.send_email_to_secretariat([email_list[1]]) + self.send_email_to_wgchairs(email_list[2:]) + self.next_form.set_message('success', 'Email sent successfully') + + +def add_form_factory(request, wg, user): + if request.method != 'POST': + return AddDelegateForm(wg=wg, user=user) + + if request.POST.get('form_type', None) == 'multiple': + return MultipleDelegateForm(wg=wg, user=user, data=request.POST.copy()) + elif request.POST.get('form_type', None) == 'notexist': + return NotExistDelegateForm(wg=wg, user=user, data=request.POST.copy()) + elif request.POST.get('form_type', None) == 'single': + return AddDelegateForm(wg=wg, user=user, data=request.POST.copy()) + + return AddDelegateForm(wg=wg, user=user) diff --git a/ietf/wgchairs/templatetags/__init__.py b/ietf/wgchairs/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ietf/wgchairs/templatetags/wgchairs_tags.py b/ietf/wgchairs/templatetags/wgchairs_tags.py new file mode 100644 index 000000000..abe633974 --- /dev/null +++ b/ietf/wgchairs/templatetags/wgchairs_tags.py @@ -0,0 +1,19 @@ +from django import template + +from ietf.wgchairs.accounts import (can_manage_workflow_in_group, + can_manage_delegates_in_group) + + +register = template.Library() + + +@register.inclusion_tag('wgchairs/wgchairs_admin_options.html', takes_context=True) +def wgchairs_admin_options(context, wg): + request = context.get('request', None) + user = request and request.user + return {'user': user, + 'can_manage_delegates': can_manage_delegates_in_group(user, wg), + 'can_manage_workflow': can_manage_workflow_in_group(user, wg), + 'wg': wg, + 'selected': context.get('selected', None), + } diff --git a/ietf/wgchairs/urls.py b/ietf/wgchairs/urls.py new file mode 100644 index 000000000..916fa4ace --- /dev/null +++ b/ietf/wgchairs/urls.py @@ -0,0 +1,8 @@ +# Copyright The IETF Trust 2008, All Rights Reserved + +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('ietf.wgchairs.views', + url(r'^workflows/$', 'manage_workflow', name='manage_workflow'), + url(r'^delegates/$', 'manage_delegates', name='manage_delegates'), +) diff --git a/ietf/wgchairs/views.py b/ietf/wgchairs/views.py new file mode 100644 index 000000000..7da34a3dd --- /dev/null +++ b/ietf/wgchairs/views.py @@ -0,0 +1,38 @@ +from ietf.idtracker.models import IETFWG +from django.shortcuts import get_object_or_404, render_to_response +from django.template import RequestContext +from django.http import HttpResponseForbidden + +from ietf.wgchairs.forms import RemoveDelegateForm, add_form_factory +from ietf.wgchairs.accounts import can_manage_delegates_in_group + + +def manage_delegates(request, acronym): + wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) + user = request.user + if not can_manage_delegates_in_group(user, wg): + return HttpResponseForbidden('You have no permission to access this view') + delegates = wg.wgdelegate_set.all() + add_form = add_form_factory(request, wg, user) + if request.method == 'POST': + if request.POST.get('remove', None): + form = RemoveDelegateForm(wg=wg, data=request.POST.copy()) + if form.is_valid(): + form.save() + elif add_form.is_valid(): + add_form.save() + add_form = add_form.get_next_form() + return render_to_response('wgchairs/manage_delegates.html', + {'wg': wg, + 'delegates': delegates, + 'selected': 'manage_delegates', + 'can_add': delegates.count() < 3, + 'add_form': add_form, + }, + RequestContext(request)) + + +def manage_workflow(request, acronym): + wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) + concluded = (wg.status_id != 1) + return render_to_response('wginfo/wg_charter.html', {'wg': wg, 'concluded': concluded, 'selected': 'manage_workflow'}, RequestContext(request))