From d43159cd90217d66490c18248929326365676f69 Mon Sep 17 00:00:00 2001
From: Ole Laursen <olau@iola.dk>
Date: Fri, 3 Feb 2012 16:22:31 +0000
Subject: [PATCH] Add support for incoming ADs with a new "pre-ad" role  -
 Legacy-Id: 3900

---
 ietf/idrfc/testsREDESIGN.py                 | 16 ++++++++++++++++
 ietf/idrfc/views_ballot.py                  | 12 ++++++++----
 ietf/idrfc/views_search.py                  |  9 ++++++---
 ietf/idtracker/templatetags/ietf_filters.py |  2 +-
 ietf/ietfauth/decorators.py                 |  4 ++--
 ietf/templates/wginfo/wg-dirREDESIGN.html   |  2 +-
 ietf/wgchairs/accounts.py                   |  2 +-
 ietf/wginfo/views.py                        | 11 ++++++++++-
 redesign/importing/import-roles.py          | 15 ++++++++++++---
 9 files changed, 57 insertions(+), 16 deletions(-)

diff --git a/ietf/idrfc/testsREDESIGN.py b/ietf/idrfc/testsREDESIGN.py
index b99cc3ea4..e09b4885d 100644
--- a/ietf/idrfc/testsREDESIGN.py
+++ b/ietf/idrfc/testsREDESIGN.py
@@ -500,6 +500,22 @@ class EditPositionTestCase(django.test.TestCase):
         self.assertEquals(pos.discuss, "Test discuss text")
         self.assertTrue("New position" in pos.desc)
         self.assertTrue("by Sec" in pos.desc)
+
+    def test_cannot_edit_position_as_pre_ad(self):
+        draft = make_test_data()
+        url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name))
+        
+        # transform to pre-ad
+        ad_role = Role.objects.filter(name="ad")[0]
+        ad_role.name_id = "pre-ad"
+        ad_role.save()
+
+        # we can see
+        login_testing_unauthorized(self, ad_role.person.user.username, url)
+
+        # but not touch
+        r = self.client.post(url, dict(position="discuss", discuss="Test discuss text"))
+        self.assertEquals(r.status_code, 403)
         
     def test_send_ballot_comment(self):
         draft = make_test_data()
diff --git a/ietf/idrfc/views_ballot.py b/ietf/idrfc/views_ballot.py
index 2e4d96271..a7a240171 100644
--- a/ietf/idrfc/views_ballot.py
+++ b/ietf/idrfc/views_ballot.py
@@ -3,7 +3,7 @@
 
 import re, os
 from datetime import datetime, date, time, timedelta
-from django.http import HttpResponse, HttpResponseRedirect, Http404
+from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
 from django.shortcuts import render_to_response, get_object_or_404
 from django.core.urlresolvers import reverse as urlreverse
 from django.template.loader import render_to_string
@@ -15,7 +15,7 @@ from django.conf import settings
 from ietf.utils.mail import send_mail_text, send_mail_preformatted
 from ietf.ietfauth.decorators import group_required
 from ietf.idtracker.templatetags.ietf_filters import in_group
-from ietf.ietfauth.decorators import has_role
+from ietf.ietfauth.decorators import has_role, role_required
 from ietf.idtracker.models import *
 from ietf.iesg.models import *
 from ietf.ipr.models import IprDetail
@@ -222,7 +222,7 @@ class EditPositionFormREDESIGN(forms.Form):
            raise forms.ValidationError("You must enter a non-empty discuss")
        return entered_discuss
 
-@group_required('Area_Director','Secretariat')
+@role_required('Area Director','Secretariat')
 def edit_positionREDESIGN(request, name):
     """Vote and edit discuss and comment on Internet Draft as Area Director."""
     doc = get_object_or_404(Document, docalias__name=name)
@@ -238,7 +238,7 @@ def edit_positionREDESIGN(request, name):
         return_to_url = doc.get_absolute_url()
 
     # if we're in the Secretariat, we can select an AD to act as stand-in for
-    if not has_role(request.user, "Area Director"):
+    if has_role(request.user, "Secretariat"):
         ad_id = request.GET.get('ad')
         if not ad_id:
             raise Http404()
@@ -248,6 +248,10 @@ def edit_positionREDESIGN(request, name):
     old_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time)
 
     if request.method == 'POST':
+        if not has_role(request.user, "Secretariat") and not ad.role_set.filter(name="ad", group__type="area", group__state="active"):
+            # prevent pre-ADs from voting
+            return HttpResponseForbidden("Must be a proper Area Director in an active area to cast ballot")
+        
         form = EditPositionForm(request.POST)
         if form.is_valid():
  
diff --git a/ietf/idrfc/views_search.py b/ietf/idrfc/views_search.py
index 55e15eac6..971447122 100644
--- a/ietf/idrfc/views_search.py
+++ b/ietf/idrfc/views_search.py
@@ -301,7 +301,9 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
             active_ads = list(Person.objects.filter(role__name="ad",
                                                     role__group__type="area",
                                                     role__group__state="active").distinct())
-            inactive_ads = list(Person.objects.filter(pk__in=responsible)
+            inactive_ads = list((Person.objects.filter(pk__in=responsible) | Person.objects.filter(role__name="pre-ad",
+                                                                                                  role__group__type="area",
+                                                                                                  role__group__state="active"))
                                 .exclude(pk__in=[x.pk for x in active_ads]))
             extract_last_name = lambda x: x.name_parts()[3]
             active_ads.sort(key=extract_last_name)
@@ -568,10 +570,11 @@ def by_ad(request, name):
     ad_name = None
     if settings.USE_DB_REDESIGN_PROXY_CLASSES:
         responsible = Document.objects.values_list('ad', flat=True).distinct()
-        for p in Person.objects.filter(Q(role__name="ad",
+        for p in Person.objects.filter(Q(role__name__in=("pre-ad", "ad"),
                                          role__group__type="area",
                                          role__group__state="active")
-                                       | Q(pk__in=responsible)):
+                                       | Q(pk__in=responsible)).distinct():
+            print name, p.full_name_as_key()
             if name == p.full_name_as_key():
                 ad_id = p.id
                 ad_name = p.plain_name()
diff --git a/ietf/idtracker/templatetags/ietf_filters.py b/ietf/idtracker/templatetags/ietf_filters.py
index 4bb35bc49..f01f06f6d 100644
--- a/ietf/idtracker/templatetags/ietf_filters.py
+++ b/ietf/idtracker/templatetags/ietf_filters.py
@@ -436,7 +436,7 @@ def stable_dictsort(value, arg):
 def ad_area(user):
     if user and user.is_authenticated():
         from ietf.group.models import Group
-        g = Group.objects.filter(role__name="ad", role__person__user=user)
+        g = Group.objects.filter(role__name__in=("pre-ad", "ad"), role__person__user=user)
         if g:
             return g[0].acronym
     return None
diff --git a/ietf/ietfauth/decorators.py b/ietf/ietfauth/decorators.py
index a5d74e12f..a599bc746 100644
--- a/ietf/ietfauth/decorators.py
+++ b/ietf/ietfauth/decorators.py
@@ -79,7 +79,7 @@ def has_role(user, role_names):
         return False
 
     role_qs = {
-        "Area Director": Q(person=person, name="ad", group__type="area", group__state="active"),
+        "Area Director": Q(person=person, name__in=("pre-ad", "ad"), group__type="area", group__state="active"),
         "Secretariat": Q(person=person, name="secr", group__acronym="secretariat"),
         "IANA": Q(person=person, name="auth", group__acronym="iana"),
         "WG Chair": Q(person=person,name="chair", group__type="wg", group__state="active"),
@@ -97,7 +97,7 @@ def role_required(*role_names):
     """View decorator for checking that the user is logged in and
     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)))
+                                 "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
diff --git a/ietf/templates/wginfo/wg-dirREDESIGN.html b/ietf/templates/wginfo/wg-dirREDESIGN.html
index 212528123..a74f48ba9 100644
--- a/ietf/templates/wginfo/wg-dirREDESIGN.html
+++ b/ietf/templates/wginfo/wg-dirREDESIGN.html
@@ -58,7 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    <p>Area Director{{ forloop.revcounter|pluralize }}:</p>
    <table style="margin-left: 2em" class="ietf-wg-table">
       {% endif %}
-      <tr><td><span class="square bgcolor{{forloop.counter}}">&nbsp;</span></td> <td><a href="mailto:{{ ad.address }}">{{ ad.person.plain_name }} &lt;{{ ad.address }}&gt;</a></td></tr>
+      <tr><td><span class="square bgcolor{{forloop.counter}}">&nbsp;</span></td> <td><a href="mailto:{{ ad.address }}">{{ ad.person.plain_name }} &lt;{{ ad.address }}&gt;</a>{% if ad.incoming %} (Incoming AD){% endif %}</td></tr>
       {% if forloop.last %}
    </table>
    {% endif %}
diff --git a/ietf/wgchairs/accounts.py b/ietf/wgchairs/accounts.py
index 0741f1189..e668edefe 100644
--- a/ietf/wgchairs/accounts.py
+++ b/ietf/wgchairs/accounts.py
@@ -11,7 +11,7 @@ def is_area_director_for_group(person, group):
     return bool(group.area.area.areadirector_set.filter(person=person).count())
 
 def is_area_director_for_groupREDESIGN(person, group):
-    return bool(Role.objects.filter(group=group.parent, person=person, name="ad"))
+    return bool(Role.objects.filter(group=group.parent, person=person, name=("ad", "pre-ad")))
 
 
 def is_group_chair(person, group):
diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py
index be68ec5db..85fb51835 100644
--- a/ietf/wginfo/views.py
+++ b/ietf/wginfo/views.py
@@ -99,7 +99,16 @@ def wg_dirREDESIGN(request):
     
     areas = Group.objects.filter(type="area", state="active").order_by("name")
     for area in areas:
-        area.ads = sorted(Email.objects.filter(role__group=area, role__name="ad").select_related("person"), key=lambda e: e.person.name_parts()[3])
+        area.ads = []
+        for e in Email.objects.filter(role__group=area, role__name="ad").select_related("person"):
+            e.incoming = False
+            area.ads.append(e)
+
+        for e in Email.objects.filter(role__group=area, role__name="pre-ad").select_related("person"):
+            e.incoming = True
+            area.ads.append(e)
+
+        area.ads.sort(key=lambda e: (e.incoming, e.person.name_parts()[3]))
         area.wgs = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym")
         area.urls = area.groupurl_set.all().order_by("name")
         for wg in area.wgs:
diff --git a/redesign/importing/import-roles.py b/redesign/importing/import-roles.py
index 63283e60e..bfae4c62b 100755
--- a/redesign/importing/import-roles.py
+++ b/redesign/importing/import-roles.py
@@ -33,6 +33,7 @@ from ietf.utils.history import *
 # SDOAuthorizedIndividual, WGDelegate
 
 area_director_role = name(RoleName, "ad", "Area Director")
+pre_area_director_role = name(RoleName, "pre-ad", "Incoming Area Director")
 chair_role = name(RoleName, "chair", "Chair")
 editor_role = name(RoleName, "editor", "Editor")
 secretary_role = name(RoleName, "secr", "Secretary")
@@ -237,14 +238,22 @@ for o in AreaDirector.objects.all():
     
     area = Group.objects.get(acronym=o.area.area_acronym.acronym)
 
-    r = Role.objects.filter(name=area_director_role,
+    role_type = area_director_role
+    
+    try:
+        if IESGLogin.objects.get(person=o.person).user_level == 4:
+            role_type = pre_area_director_role
+    except IESGLogin.DoesNotExist:
+        pass
+    
+    r = Role.objects.filter(name=role_type,
                             person=email.person)
     if r and r[0].group == "iesg":
         r[0].group = area
-        r[0].name = area_director_role
+        r[0].name = role_type
         r[0].save()
     else:
-        Role.objects.get_or_create(name=area_director_role, group=area, person=email.person, email=email)
+        Role.objects.get_or_create(name=role_type, group=area, person=email.person, email=email)
 
 # IESGHistory
 emails_for_time = {}