From cca4924597ff7ffa8e7b9bf5a69d073c6d5ce508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Jim=C3=A9nez?= Date: Sat, 29 Dec 2012 00:52:52 +0000 Subject: [PATCH] Create views skel for private area. Create new decorators to test if user is nocom member News function in Group model to get the group members Move util functions to utils.py module Improve passes_test_decorator function so test_func accept more params. See #919 - Legacy-Id: 5161 --- ietf/group/models.py | 14 +++++++++- ietf/ietfauth/decorators.py | 4 +-- ietf/nomcom/decorators.py | 16 +++++++++++ ietf/nomcom/urls.py | 2 ++ ietf/nomcom/utils.py | 27 ++++++++++++++----- ietf/nomcom/views.py | 27 +++++++++++++++++-- .../templates/nomcom/nomcom_private_base.html | 16 +++++++++++ ietf/templates/nomcom/private_index.html | 10 +++++++ ietf/templates/nomcom/private_merge.html | 9 +++++++ 9 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 ietf/nomcom/decorators.py create mode 100644 ietf/templates/nomcom/nomcom_private_base.html create mode 100644 ietf/templates/nomcom/private_index.html create mode 100644 ietf/templates/nomcom/private_merge.html diff --git a/ietf/group/models.py b/ietf/group/models.py index 84ef3c479..8ae243376 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -1,6 +1,8 @@ # Copyright The IETF Trust 2007, All Rights Reserved from django.db import models +from django.db.models import Q + from ietf.name.models import * from ietf.person.models import Email, Person @@ -53,10 +55,19 @@ class Group(GroupInfo): else: return False + def is_member(self, user): + members = self.get_members() + users = [member.person.user for member in members] + return user in users + def get_chair(self): chair = self.role_set.filter(name__slug='chair')[:1] return chair and chair[0] or None + def get_members(self): + members = self.role_set.filter(Q(name__slug='member') | Q(name__slug='chair')) + return members + class GroupHistory(GroupInfo): group = models.ForeignKey(Group, related_name='history_set') acronym = models.CharField(max_length=40) @@ -68,8 +79,9 @@ class GroupURL(models.Model): group = models.ForeignKey(Group) name = models.CharField(max_length=255) url = models.URLField(verify_exists=False) + def __unicode__(self): - return u"%s (%s)" % (self.url, self.name) + return u"%s (%s)" % (self.url, self.name) class GroupMilestone(models.Model): group = models.ForeignKey(Group) diff --git a/ietf/ietfauth/decorators.py b/ietf/ietfauth/decorators.py index 933338a43..965ea1f14 100644 --- a/ietf/ietfauth/decorators.py +++ b/ietf/ietfauth/decorators.py @@ -47,7 +47,7 @@ def passes_test_decorator(test_func, message): def inner(request, *args, **kwargs): if not request.user.is_authenticated(): return HttpResponseRedirect('%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path()))) - elif test_func(request.user): + elif test_func(request.user, *args, **kwargs): return view_func(request, *args, **kwargs) else: return HttpResponseForbidden(message) @@ -109,7 +109,7 @@ def role_required(*role_names): has one of the listed roles.""" return passes_test_decorator(lambda u: has_role(u, role_names), "Restricted to role%s %s" % ("s" if len(role_names) != 1 else "", ", ".join(role_names))) - + if settings.USE_DB_REDESIGN_PROXY_CLASSES: # overwrite group_required group_required = lambda *group_names: role_required(*[n.replace("Area_Director", "Area Director") for n in group_names]) diff --git a/ietf/nomcom/decorators.py b/ietf/nomcom/decorators.py new file mode 100644 index 000000000..d8368420d --- /dev/null +++ b/ietf/nomcom/decorators.py @@ -0,0 +1,16 @@ +from ietf.ietfauth.decorators import passes_test_decorator + +from ietf.nomcom.utils import get_nomcom_by_year + + +def member_required(role=None): + def _is_nomcom_member(user, *args, **kwargs): + year = kwargs.get('year', None) + if year: + nomcom = get_nomcom_by_year(year=year) + if role == 'chair': + return nomcom.group.is_chair(user) + else: + return nomcom.group.is_member(user) + return False + return passes_test_decorator(_is_nomcom_member, 'Restricted to NomCom %s' % role) diff --git a/ietf/nomcom/urls.py b/ietf/nomcom/urls.py index f6f21cd9d..17947ae7f 100644 --- a/ietf/nomcom/urls.py +++ b/ietf/nomcom/urls.py @@ -3,6 +3,8 @@ from ietf.nomcom.forms import EditChairForm, EditChairFormPreview, \ EditMembersForm, EditMembersFormPreview urlpatterns = patterns('ietf.nomcom.views', + url(r'^(?P\d{4})/private/$', 'private_index', name='nomcom_private_index'), + url(r'^(?P\d{4})/private/merge/$', 'private_merge', name='nomcom_private_merge'), url(r'^(?P\d{4})/$', 'index', name='nomcom_index'), url(r'^(?P\d{4})/requirements/$', 'requirements', name='nomcom_requirements'), url(r'^(?P\d{4})/questionnaires/$', 'questionnaires', name='nomcom_questionnaires'), diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py index 3da455d9f..2f44ed6d8 100644 --- a/ietf/nomcom/utils.py +++ b/ietf/nomcom/utils.py @@ -1,4 +1,5 @@ from django.shortcuts import get_object_or_404 +from django.core.exceptions import PermissionDenied from ietf.dbtemplate.models import DBTemplate @@ -12,6 +13,25 @@ NOMINATION_EMAIL_TEMPLATE = 'email/new_nomination.txt' DEFAULT_NOMCOM_TEMPLATES = [HOME_TEMPLATE, INEXISTENT_PERSON_TEMPLATE, NOMINATION_EMAIL_TEMPLATE, NOMINEE_EMAIL_TEMPLATE] +def get_nomcom_by_year(year): + from ietf.nomcom.models import NomCom + return get_object_or_404(NomCom, + group__acronym__icontains=year, + group__state__slug='active') + + +def is_nomcom_member(user, nomcom): + is_group_member = nomcom.group.is_member(user) + if not is_group_member: + raise PermissionDenied("Must be nomcom member") + + +def is_nomcom_chair(user, nomcom): + is_group_chair = nomcom.group.is_chair(user) + if not is_group_chair: + raise PermissionDenied("Must be nomcom chair") + + def initialize_templates_for_group(group): for template_name in DEFAULT_NOMCOM_TEMPLATES: template_path = MAIN_NOMCOM_TEMPLATE_PATH + template_name @@ -47,10 +67,3 @@ def initialize_requirements_for_position(position): variables=template.variables, type_id=template.type_id, content=template.content) - - -def get_nomcom_by_year(year): - from ietf.nomcom.models import NomCom - return get_object_or_404(NomCom, - group__acronym__icontains=year, - group__state__slug='active') diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index b003bb1a6..ebbfab7f7 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -7,7 +7,9 @@ from django.template.loader import render_to_string from django.utils import simplejson -from ietf.nomcom.utils import get_nomcom_by_year, HOME_TEMPLATE +from ietf.nomcom.utils import get_nomcom_by_year, is_nomcom_member, \ + is_nomcom_chair, HOME_TEMPLATE +from ietf.nomcom.decorators import member_required from ietf.nomcom.forms import EditPublicKeyForm, NominateForm from ietf.nomcom.models import Position @@ -23,6 +25,26 @@ def index(request, year): 'template': template}, RequestContext(request)) +@member_required(role='chair') +def private_index(request, year): + nomcom = get_nomcom_by_year(year) + is_nomcom_member(request.user, nomcom) + return render_to_response('nomcom/private_index.html', + {'nomcom': nomcom, + 'year': year, + 'selected': 'index'}, RequestContext(request)) + + +@member_required(role='member') +def private_merge(request, year): + nomcom = get_nomcom_by_year(year) + is_nomcom_member(request.user, nomcom) + return render_to_response('nomcom/private_merge.html', + {'nomcom': nomcom, + 'year': year, + 'selected': 'merge'}, RequestContext(request)) + + def requirements(request, year): nomcom = get_nomcom_by_year(year) positions = nomcom.position_set.all() @@ -72,9 +94,10 @@ def comments(request, year): 'selected': 'comments'}, RequestContext(request)) -@login_required +@member_required(role='chair') def edit_publickey(request, year): nomcom = get_nomcom_by_year(year) + is_nomcom_chair(request.user, nomcom) is_group_chair = nomcom.group.is_chair(request.user) if not is_group_chair: return HttpResponseForbidden("Must be group chair") diff --git a/ietf/templates/nomcom/nomcom_private_base.html b/ietf/templates/nomcom/nomcom_private_base.html new file mode 100644 index 000000000..ee69d3aa4 --- /dev/null +++ b/ietf/templates/nomcom/nomcom_private_base.html @@ -0,0 +1,16 @@ +{% extends "nomcom/nomcom_base.html" %} + +{% block content %} + +

Nomcom {{ year }} Private Area

+ +
+ {% if selected == "index" %}List of nominees{% else %}List of nominees{% endif %} | + {% if selected == "merge" %}Merge nominee email addr{% else %}Merge nominee email addr{% endif %} | +
+ + + {% block nomcom_content %} + {% endblock %} + +{% endblock %} diff --git a/ietf/templates/nomcom/private_index.html b/ietf/templates/nomcom/private_index.html new file mode 100644 index 000000000..5d5b71603 --- /dev/null +++ b/ietf/templates/nomcom/private_index.html @@ -0,0 +1,10 @@ +{% extends "nomcom/nomcom_private_base.html" %} + +{% block subtitle %} - Administration {% endblock %} + +{% block nomcom_content %} + +

Nomine administration

+

The following is a list of registered nominees. (You can request confirmation from nominees if they haven't +replied to the nomination notification they have received.)

+{% endblock %} diff --git a/ietf/templates/nomcom/private_merge.html b/ietf/templates/nomcom/private_merge.html new file mode 100644 index 000000000..809758ccd --- /dev/null +++ b/ietf/templates/nomcom/private_merge.html @@ -0,0 +1,9 @@ +{% extends "nomcom/nomcom_private_base.html" %} + +{% block subtitle %} - Merging nominee email addresses {% endblock %} + +{% block nomcom_content %} + +

Merging nominee email addresses

+

If a nominee has been nominated with multiple email addresses, the nominee will appear multiple times in the nomination list, as the email address is used as the unique identifier for each nominee. In order to permit comments and nominations to be submitted under multiple email addresses, there is a list of secondary email addresses which needs to be kept up-to-date. When nominations of one particular nominee have already been made under different email addresses, the nomination comments from the secondary address also needs to be merged with those under the primary address. It doesn't matter particularly which email address is used as primary, as far as the nominee information maintenance goes, but it's probably handier for the nomcom if the primary address is the one which the nominee prefers at the time.

+{% endblock %}