improved request validation
authorJordan Augé <jordan.auge@lip6.fr>
Wed, 28 Aug 2013 08:41:36 +0000 (10:41 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Wed, 28 Aug 2013 08:41:36 +0000 (10:41 +0200)
manifold/manifoldapi.py
portal/actions.py [new file with mode: 0644]
portal/forms.py
portal/migrations/0003_extend_slice.py [new file with mode: 0644]
portal/models.py
portal/static/css/validate_pending.css
portal/templates/slice_request_email.txt [new file with mode: 0644]
portal/templates/user_request_email.txt [new file with mode: 0644]
portal/templates/validate_pending.html
portal/urls.py
portal/views.py

index 81a2caf..e7d6a1b 100644 (file)
@@ -74,5 +74,10 @@ def execute_query(request, query):
     print "-"*80
     result = manifold_api.forward(query.to_dict())
     if result['code'] == 2:
-        raise Exception, 'Error running query'
+        raise Exception, 'Error running query: %r' % result
+
+    # XXX Handle errors
+    #Error running query: {'origin': [0, 'XMLRPCAPI'], 'code': 2, 'description': 'No such session: No row was found for one()', 'traceback': 'Traceback (most recent call last):\n  File "/usr/local/lib/python2.7/dist-packages/manifold/core/xmlrpc_api.py", line 68, in xmlrpc_forward\n    user = Auth(auth).check()\n  File "/usr/local/lib/python2.7/dist-packages/manifold/auth/__init__.py", line 245, in check\n    return self.auth_method.check()\n  File "/usr/local/lib/python2.7/dist-packages/manifold/auth/__init__.py", line 95, in check\n    raise AuthenticationFailure, "No such session: %s" % e\nAuthenticationFailure: No such session: No row was found for one()\n', 'type': 2, 'ts': None, 'value': None}
+
+
     return result['value'] 
diff --git a/portal/actions.py b/portal/actions.py
new file mode 100644 (file)
index 0000000..16ddaa1
--- /dev/null
@@ -0,0 +1,180 @@
+from django.http                 import HttpResponse
+from manifold.core.query         import Query
+from manifold.manifoldapi        import execute_query
+from portal.models               import PendingUser, PendingSlice
+import json
+
+# Get the list of authorities
+
+def authority_get_pis(authority_hrn):
+    query = Query.get('authority').filter_by('authority_hrn', '==', authority_hrn).select('pi_users')
+    results = execute_query(query)
+    if not results:
+        raise Exception, "Authority not found: %s" % authority_hrn
+    result, = results
+    return result['pi_users']
+
+def authority_get_pi_emails(authority_hrn):
+    user_hrns = authority_get_pis(authority_hrn)
+    
+    query = Query.get('user').filter_by('user_hrn', 'included', user_hrns).select('user_email')
+    results = execute_query(query)
+    
+    return [result['user_email'] for result in results]
+
+# SFA add record (user, slice)
+
+def sfa_add_user(user_params):
+    # sfi.py add --xrn=fed4fire.upmc.timur_friedman --type=user --key=/root/.sfi/timur.pub --email=timur.friedman@lip6.fr --extra=first_name=Timur --extra=last_name=Friedman --extra=enabled=true
+    # user_params: xrn type key email + first_name last_name enabled
+    query = Query.create('user').set(user_params).select('user_hrn')
+    results = execute_query(query)
+    if not results:
+        raise Exception, "Failed creating SFA user: %s" % user_params['user_hrn']
+    result, = results
+    return result['user_hrn']
+
+def sfa_add_slice(slice_params):
+    pass
+
+# Propose hrn
+
+def manifold_add_user(user_params):
+    # user_params: email, password
+    query = Query.create('local:user').set(user_params).select('email')
+    results = execute_query(query)
+    if not results:
+        raise Exception, "Failed creating manifold user: %s" % user_params['email']
+    result, = results
+    return result['email']
+
+def manifold_add_account(account_params):
+    query = Query.create('local:account').set(account_params).select(['user', 'platform'])
+    results = execute_query(query)
+    if not results:
+        raise Exception, "Failed creating manifold account on platform %s for user: %s" % (account_params['platform'], account_params['user'])
+    result, = results
+    return (result['user'], result['platform'])
+
+def make_request_user(user):
+    request = {}
+    request['type'] = 'user'
+    request['id'] = user.id
+    request['timestamp'] = 'TODO' # XXX in DB ?
+    request['authority_hrn'] = user.authority_hrn
+    request['first_name'] = user.first_name
+    request['last_name'] = user.last_name
+    request['email'] = user.email
+    return request
+
+def make_request_slice(slice):
+    request = {}
+    request['type'] = 'slice'
+    request['id'] = slice.id
+    request['timestamp'] = 'TODO' # XXX in DB ?
+    request['authority_hrn'] = slice.authority_hrn
+    request['number_of_nodes'] = slice.number_of_nodes
+    request['type_of_nodes'] = slice.type_of_nodes
+    request['purpose'] = slice.purpose
+    return request
+
+def make_requests(pending_users, pending_slices):
+    print "pending users =", pending_users
+    print "pending slices =", pending_slices
+
+    requests = []
+    for user in pending_users:
+        requests.append(make_request_user(user))
+    for slice in pending_slices:
+        requests.append(make_request_slice(slice))
+    return requests   
+
+def get_request_by_id(ids):
+    sorted_ids = { 'user': [], 'slice': [] }
+    for type__id in ids:
+        type, id = type__id.split('__')
+        sorted_ids[type].append(id)
+        
+    if not ids:
+        pending_users  = PendingUser.objects.all()
+        pending_slices = PendingSlice.objects.all()
+    else:
+        pending_users  = PendingUser.objects.filter(id__in=sorted_ids['user']).all()
+        pending_slices = PendingSlice.objects.filter(id__in=sorted_ids['slice']).all()
+
+    return make_requests(pending_users, pending_slices)
+
+def get_request_by_authority(authority_hrns):
+    if not authority_hrns:
+        pending_users  = PendingUser.objects.all()
+        pending_slices = PendingSlice.objects.all()
+    else:
+        pending_users  = PendingUser.objects.filter(authority_hrn__in=authority_hrns).all()
+        pending_slices = PendingSlice.objects.filter(authority_hrn__in=authority_hrns).all()
+
+    return make_requests(pending_users, pending_slices)
+    
+SFA_USER_KEYS         = ['xrn', 'type', 'key', 'first_name', 'last_name', 'email']
+SFA_SLICE_KEYS        = []
+MANIFOLD_USER_KEYS    = ['email', 'password']
+MANIFOLD_ACCOUNT_KEYS = []
+
+def portal_validate_request(request_ids):
+    status = {}
+
+    if not isinstance(request_ids, list):
+        request_ids = [request_ids]
+
+    requests = get_request_by_id(request_ids)
+    for request in requests:
+        # type, id, timestamp, details, allowed -- MISSING: authority_hrn
+        # CAREFUL about details
+        # user  : first name, last name, email, password, keypair
+        # slice : number of nodes, type of nodes, purpose
+        
+        request_status = {}
+
+        if request['type'] == 'user':
+            try:
+                sfa_user_params = { key: request[key] for key in SFA_USER_KEYS }
+                sfa_user_params['enabled'] = True
+                # XXX # sfa_add_user(sfa_user_params)
+                request_status['SFA user'] = {'status': True }
+            except Exception, e:
+                request_status['SFA user'] = {'status': False, 'description': str(e)}
+
+            try:
+                manifold_user_params = { key: request[key] for key in MANIFOLD_USER_KEYS }
+                # XXX # manifold_add_user(manifold_user_params)
+                request_status['MySlice user'] = {'status': True }
+            except Exception, e:
+                request_status['MySlice user'] = {'status': False, 'description': str(e)}
+
+            # XXX
+            #manifold_account_params = { key: request[key] for key in MANIFOLD_ACCOUNT_KEYS }
+            #manifold_add_account(manifold_account_params)
+            request_status['MySlice testbed accounts'] = {'status': False }
+
+        elif request['type'] == 'slice':
+            try:
+                sfa_slice_params = { key: request[key] for key in SFA_SLICE_KEYS }
+                # XXX # sfa_add_slice(sfa_slice_params)
+                request_status['SFA slice'] = {'status': True }
+            except Exception, e:
+                request_status['SFA slice'] = {'status': False, 'description': str(e)}
+
+        status['%s__%s' % (request['type'], request['id'])] = request_status
+
+    # XXX remove from database succeeded actions
+
+    return status
+
+
+def validate_action(*args, **kwargs):
+    ids = filter(None, kwargs['id'].split('/'))
+    status = portal_validate_request(ids)
+    json_answer = json.dumps(status)
+    return HttpResponse (json_answer, mimetype="application/json")
+
+# Django and ajax
+# http://djangosnippets.org/snippets/942/
index 46a60bd..7abc0e4 100644 (file)
@@ -105,12 +105,12 @@ from django.utils.translation import ugettext_lazy as _
 # DEPRECATED # 
 # DEPRECATED #    class Meta:
 # DEPRECATED #        model = PendingUser
-
-class SliceRequestForm(forms.ModelForm):
-    slice_name = forms.CharField( widget=forms.TextInput )
-    class Meta:
-        model = PendingSlice
-
+# DEPRECATED #
+# DEPRECATED #class SliceRequestForm(forms.ModelForm):
+# DEPRECATED #    slice_name = forms.CharField( widget=forms.TextInput )
+# DEPRECATED #    class Meta:
+# DEPRECATED #        model = PendingSlice
+# DEPRECATED #
 # DEPRECATED #class RegisterUserStep2Form(forms.ModelForm):
 # DEPRECATED #    class Meta:
 # DEPRECATED #        model = PendingUser
@@ -126,10 +126,29 @@ class ContactForm(forms.Form):
 
 class SliceRequestForm(forms.Form):
     slice_name = forms.CharField()
+    authority_hrn = forms.ChoiceField(choices=[(1, 'un')])
     number_of_nodes  = forms.DecimalField()
     type_of_nodes = forms.CharField()
     purpose = forms.CharField(widget=forms.Textarea)
     email = forms.EmailField()
     cc_myself = forms.BooleanField(required=False)
 
+    def __init__(self, *args, **kwargs):
+        initial =  kwargs.get('initial', {})
+        authority_hrn = initial.get('authority_hrn', None)
+
+        # set just the initial value
+        # in the real form needs something like this {'authority_hrn':'a'}
+        # but in this case you want {'authority_hrn':('a', 'letter_a')}
+        if authority_hrn:
+            kwargs['initial']['authority_hrn'] = authority_hrn[0]
+
+        # create the form
+        super(SliceRequestForm, self).__init__(*args, **kwargs)
+
+        # self.fields only exist after, so a double validation is needed
+        if authority_hrn:# and authority_hrn[0] not in (c[0] for c in authority_hrn):
+            # XXX This does not work, the choicefield is not updated...
+            #self.fields['authority_hrn'].choices.extend(authority_hrn)
+            self.fields['authority_hrn'] = forms.ChoiceField( choices=authority_hrn)
     
diff --git a/portal/migrations/0003_extend_slice.py b/portal/migrations/0003_extend_slice.py
new file mode 100644 (file)
index 0000000..2437098
--- /dev/null
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'PendingSlice.number_of_nodes'
+        db.add_column(u'portal_pendingslice', 'number_of_nodes',
+                      self.gf('django.db.models.fields.TextField')(default=0),
+                      keep_default=False)
+
+        # Adding field 'PendingSlice.type_of_nodes'
+        db.add_column(u'portal_pendingslice', 'type_of_nodes',
+                      self.gf('django.db.models.fields.TextField')(default='NA'),
+                      keep_default=False)
+
+        # Adding field 'PendingSlice.purpose'
+        db.add_column(u'portal_pendingslice', 'purpose',
+                      self.gf('django.db.models.fields.TextField')(default='NA'),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'PendingSlice.number_of_nodes'
+        db.delete_column(u'portal_pendingslice', 'number_of_nodes')
+
+        # Deleting field 'PendingSlice.type_of_nodes'
+        db.delete_column(u'portal_pendingslice', 'type_of_nodes')
+
+        # Deleting field 'PendingSlice.purpose'
+        db.delete_column(u'portal_pendingslice', 'purpose')
+
+
+    models = {
+        u'portal.institution': {
+            'Meta': {'object_name': 'Institution'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.TextField', [], {})
+        },
+        u'portal.pendingslice': {
+            'Meta': {'object_name': 'PendingSlice'},
+            'authority_hrn': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number_of_nodes': ('django.db.models.fields.TextField', [], {'default': '0'}),
+            'purpose': ('django.db.models.fields.TextField', [], {'default': "'NA'"}),
+            'slice_name': ('django.db.models.fields.TextField', [], {}),
+            'type_of_nodes': ('django.db.models.fields.TextField', [], {'default': "'NA'"})
+        },
+        u'portal.pendinguser': {
+            'Meta': {'object_name': 'PendingUser'},
+            'affiliation': ('django.db.models.fields.TextField', [], {}),
+            'authority_hrn': ('django.db.models.fields.TextField', [], {}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'first_name': ('django.db.models.fields.TextField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'keypair': ('django.db.models.fields.TextField', [], {}),
+            'last_name': ('django.db.models.fields.TextField', [], {}),
+            'password': ('django.db.models.fields.TextField', [], {})
+        }
+    }
+
+    complete_apps = ['portal']
\ No newline at end of file
index a16e330..9a414ba 100644 (file)
@@ -309,5 +309,8 @@ class PendingUser(models.Model):
 
 
 class PendingSlice(models.Model):
-    slice_name  = models.TextField()
-    authority_hrn = models.TextField(null=True)
+    slice_name      = models.TextField()
+    authority_hrn   = models.TextField(null=True)
+    number_of_nodes = models.TextField(default=0)
+    type_of_nodes   = models.TextField(default='NA')
+    purpose         = models.TextField(default='NA')
index 4ed27e1..d0a7a56 100644 (file)
@@ -5,7 +5,7 @@
        position:relative;
 }
 
-.portal_validate_request .slice {
+.portal_validate_request.slice {
        background-image: url(../img/slice-icon.png);
 }
 
@@ -13,7 +13,7 @@
        background-image: url(../img/user-icon.png);
 }
 
-.portal_validate_request .authority {
+.portal_validate_request.authority {
        background-image: url(../img/authority-icon.png);
 }
 
 }
 
 .portal_validate_request .id {
-       position: relative;
+       position: absolute;
        left: 5%;
 }
-.portal_validate_request .timestamp {
-       position: relative;
-       left: 10%;
+
+.portal_validate_request .authority {
+       position: absolute;
+       left: 7%;
 }
+
 .portal_validate_request .details {
-       position: relative;
-       left: 20%;
+       position: absolute;
+       left: 15%;
+}
+
+.portal_validate_request .timestamp {
+       position: absolute;
+       left: 50%;
+}
+
+.portal_validate_request .status {
+       position: absolute;
+       left: 65%;
 }
 
 .portal_validate_request input {
-       position: relative;
-       left: 40%;
+       position: absolute;
+       left: 60%;
 }
 
 .portal_validate_request.even {
diff --git a/portal/templates/slice_request_email.txt b/portal/templates/slice_request_email.txt
new file mode 100644 (file)
index 0000000..ef46a04
--- /dev/null
@@ -0,0 +1,9 @@
+NEW SLICE REQUEST
+
+slice name      : {{slice_name}}
+number of nodes : {{number_of_nodes}}
+type of nodes   : {{type_of_nodes}}
+purpose         : {{purpose}}
+email           : {{email}}
+cc myself       : {{cc_myself}}
+
diff --git a/portal/templates/user_request_email.txt b/portal/templates/user_request_email.txt
new file mode 100644 (file)
index 0000000..3e18d0e
--- /dev/null
@@ -0,0 +1,9 @@
+NEW USER REQUEST
+
+first_name   : {{first_name}}
+last_name    : {{last_name}} 
+authority_hrn: {{authority_hrn}}
+keypair      : {{keypair}}
+email        : {{email}}
+cc myself    : {{cc_myself}}
+
index dd7d5c2..74176ea 100644 (file)
@@ -2,6 +2,47 @@
 
 {% block head %}
 <link rel="stylesheet" type="text/css" href="{{STATIC_URL}}/css/validate_pending.css" />
+<script type="text/javascript">
+       function on_click_event() {
+               var ids = []; 
+               $('.portal__validate__checkbox').each(function(i, el) {
+                       if ($(el).attr('checked')) {
+                               // portal__validate__checkbox__slice__2
+                               var id_array = $(el).attr('id').split('__');
+                               // push(slice__2)
+                               ids.push(id_array[3] + '__' + id_array[4]);
+                       }
+               });
+               if (ids.length > 0) {
+                       var id_str = ids.join('/');
+
+                       // XXX spinner
+
+                       $.getJSON('/portal/validate_action/' + id_str,
+                               function(status) {
+                                       $.each(status, function(request_type__id, request_status) {
+                                               // request_status: NAME -> dict (status, description)
+                                               var status_str = '';
+                                               $.each(request_status, function(name, result) {
+                                                       if (status_str != '')
+                                                               status_str += ' -- ';
+
+                                                       if (result.status) {
+                                                               status_str += '<font color="green">' + name + '</font>';
+                                                               $('#portal__validate__checkbox__' + request_type__id).hide();
+                                                       } else {
+                                                               status_str += '<font color="red">' + name + ' (' + result.description + ')</font>';
+                                                       }
+                                               });
+                                               $('#portal__status__' + request_type__id).html(status_str)
+
+
+                                       });
+                               }
+                       );
+               }
+       }
+</script>
 {% endblock %}
 
 {% block unfold1_main %}
                <span class='type'>{{ request.type }}</span>
                <span class='id'>{{ request.id }}</span>
                <span class='timestamp'>{{ request.timestamp }}</span>
-               <span class='details'>{{ request.details }}</span>
+               <span class='authority'>{{ request.authority_hrn }}</span>
+        
+        {% if request.type == 'user' %}
+        <span class='details'>First name: {{request.first_name}} -- Last name: {{request.last_name}} -- Email: {{request.email}}</span>
+        {% else %}
+            {% if request.type == 'slice' %}
+        <span class='details'>Number of nodes: {{request.number_of_nodes}} -- Type of nodes: {{request.type_of_nodes}} -- Purpose: {{request.purpose}}</span>
+            {% else %} {# authority #}
+        <span class='details'>TODO</span>
+            {% endif %}
+        {% endif %}
+
                {% if request.allowed == 'allowed' %}
-               <input type='checkbox'/>
+               <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
                {% else %}
                        {% if request.allowed == 'expired' %}
                                expired
@@ -29,6 +81,7 @@
                                denied
                        {% endif %}
                {% endif %}
+               <span class='status' id='portal__status__{{request.type}}__{{request.id}}'></span>
        </div>
     {% endfor %}
 {% endfor %}
                <span class='timestamp'>{{ request.timestamp }}</span>
                <span class='details'>{{ request.details }}</span>
                {% if request.allowed == 'allowed' %}
-               <input type='checkbox'/>
+               <input class='portal__validate__checkbox' id='portal__validate__checkbox__{{request.type}}__{{request.id}}' type='checkbox'/>
                {% else %}
                        {% if request.allowed == 'expired' %}
                                expired
                                denied
                        {% endif %}
                {% endif %}
+               <span class='status' id='portal__status__{{request.type}}__{{request.id}}'></span>
        </div>
     {% endfor %}
 {% endfor %}
 
 {% endif %}
 
+<input type='button' id='portal__validate' value='Validate' onclick='on_click_event();'/>
+
 {% endblock %}
index 2887388..31c8908 100644 (file)
@@ -55,6 +55,9 @@ urlpatterns = patterns('',
     url(r'^slice_request/?$', views.slice_request),
     # Validate pending requests
     url(r'^validate/?$', ValidatePendingView.as_view()),
+    # http://stackoverflow.com/questions/2360179/django-urls-how-to-pass-a-list-of-items-via-clean-urls
+    # (r'^validate_action/(?P<constraints>[^/]+)/(?P<id>\w+)/?$', 'portal.views.pres_view_static'),
+     (r'^validate_action(?P<id>(?:/\w+)+)/?$', 'portal.actions.validate_action'),
 
     url(r'^pres_view/?$', PresViewView.as_view(), name='pres_view'),
     (r'^methods/(?P<type>\w+)/?$', 'portal.views.pres_view_methods'),
index ac4e7b8..87f6cb6 100644 (file)
@@ -27,6 +27,8 @@ from django.contrib              import messages
 from django.views.generic        import View
 from django.views.generic.base   import TemplateView
 from django.shortcuts            import render
+from django.template.loader      import render_to_string
+from django.core.mail            import send_mail
 
 from plugins.lists.simplelist    import SimpleList
 from plugins.hazelnut            import Hazelnut
@@ -38,6 +40,7 @@ from portal                      import signals
 from portal.forms                import SliceRequestForm, ContactForm
 from portal.util                 import RegistrationView, ActivationView
 from portal.models               import PendingUser, PendingSlice
+from portal.actions              import authority_get_pi_emails, get_request_by_authority
 from manifold.core.query         import Query
 from manifold.manifoldapi        import execute_query
 from unfold.page                 import Page
@@ -690,6 +693,8 @@ def register_4m_f4f(request):
     authorities = execute_query(request, authorities_query)
 
     if request.method == 'POST':
+        # We shall use a form here
+
         #get_email = PendingUser.objects.get(email)
         reg_fname = request.POST.get('firstname', '')
         reg_lname = request.POST.get('lastname', '')
@@ -763,10 +768,35 @@ def register_4m_f4f(request):
         #                email=reg_email, password=request.POST['password'], keypair=keypair)
         #b.save()
         if not errors:
-            b = PendingUser(first_name=reg_fname, last_name=reg_lname, affiliation=reg_aff,
-                            authority_hrn=reg_auth,
-                            email=reg_email, password=request.POST['password'], keypair=keypair)
+            b = PendingUser(
+                first_name=reg_fname, 
+                last_name=reg_lname, 
+                affiliation=reg_aff,
+                authority_hrn=reg_auth,
+                email=reg_email, 
+                password=request.POST['password'],
+                keypair=keypair
+            )
             b.save()
+
+            # Send email
+            ctx = {
+                first_name   : reg_fname, 
+                last_name    : reg_lname, 
+                affiliation  : reg_aff,
+                authority_hrn: reg_auth,
+                email        : reg_email, 
+                keypair      : keypair,
+                cc_myself    : True # form.cleaned_data['cc_myself']
+            }
+
+            recipients = authority_get_pi_emails(authority_hrn)
+            if ctx['cc_myself']:
+                recipients.append(ctx['email'])
+
+            msg = render_to_string('user_request_email.txt', ctx)
+            send_mail("Onelab New User request submitted", msg, email, recipients)
+
             return render(request, 'user_register_complete.html')
 
     return render(request, 'register_4m_f4f.html',{
@@ -796,6 +826,7 @@ def contact(request):
             email = form.cleaned_data['email'] # email of the sender
             cc_myself = form.cleaned_data['cc_myself']
 
+            #recipients = authority_get_pi_emails(authority_hrn)
             recipients = ['yasin.upmc@gmail.com']
             if cc_myself:
                 recipients.append(email)
@@ -815,26 +846,54 @@ def contact(request):
 
 
 def slice_request(request):
-    if request.method == 'POST': # If the form has been submitted...
-        form = SliceRequestForm(request.POST) # A form bound to the POST data
-        if form.is_valid(): # All validation rules pass
-            # Process the data in form.cleaned_data
-            slice_name = form.cleaned_data['slice_name']
+    errors = []
+
+    authorities_query = Query.get('authority').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc']).select('name', 'authority_hrn')
+    #authorities_query = Query.get('authority').select('authority_hrn')
+    authorities = execute_query(request, authorities_query)
+
+    authority_hrn_tuple = []
+    for authority in authorities:
+        authority_hrn_tuple.append((authority['authority_hrn'], authority['name']))
+    authority_hrn_initial = {'authority_hrn': authority_hrn_tuple}
+        
+    # request.POST or None ?
+    if request.method == 'POST':
+        # The form has been submitted
+        form = SliceRequestForm(request.POST, initial=authority_hrn_initial) 
+
+        if form.is_valid():
+            slice_name      = form.cleaned_data['slice_name']
+            authority_hrn   = form.cleaned_data['authority_hrn']
             number_of_nodes = form.cleaned_data['number_of_nodes']
-            type_of_nodes = form.cleaned_data['type_of_nodes']
-            purpose = form.cleaned_data['purpose']
+            type_of_nodes   = form.cleaned_data['type_of_nodes']
+            purpose         = form.cleaned_data['purpose']
+            
+            s = PendingSlice(
+                slice_name      = slice_name,
+                authority_hrn   = authority_hrn,
+                number_of_nodes = number_of_nodes,
+                type_of_nodes   = type_of_nodes,
+                purpose         = purpose
+            )
+            s.save()
+
+            # All validation rules pass; process data in form.cleaned_data
+            # slice_name, number_of_nodes, type_of_nodes, purpose
             email = form.cleaned_data['email'] # email of the sender
             cc_myself = form.cleaned_data['cc_myself']
 
-            recipients = ['yasin.upmc@gmail.com','jordan.auge@lip6.fr']
+            # The recipients are the PI of the authority
+            recipients = authority_get_pi_emails(authority_hrn)
+            #recipients = ['yasin.upmc@gmail.com','jordan.auge@lip6.fr']
             if cc_myself:
                 recipients.append(email)
+            msg = render_to_string('slice_request_email.txt', form.cleaned_data)
+            send_mail("Onelab New Slice request form submitted", msg, email, recipients)
 
-            from django.core.mail import send_mail
-            send_mail("Onelab New Slice request form submitted", [slice_name,number_of_nodes,type_of_nodes,purpose], email, recipients)
             return render(request,'slicereq_recvd.html') # Redirect after POST
     else:
-        form = SliceRequestForm() # An unbound form
+        form = SliceRequestForm(initial=authority_hrn_initial)
 
 #    template_env = {}
 #    template_env['form'] = form
@@ -1140,26 +1199,10 @@ class ValidatePendingView(TemplateView):
             queried_pending_authorities = pi_my_authorities | pi_delegation_authorities
             print "----"
             print "queried_pending_authorities = ", queried_pending_authorities
-            
-            # Pending requests + authorities
-            #pending_users = PendingUser.objects.filter(authority_hrn__in = queried_pending_authorities).all() 
-            #pending_slices = PendingSlice.objects.filter(authority_hrn__in = queried_pending_authorities).all() 
-            pending_users = PendingUser.objects.all()
-            pending_slices = PendingSlice.objects.all()
-
-            # Dispatch requests and build the proper structure for the template:
-
-            print "pending users =", pending_users
-            print "pending slices =", pending_slices
-
-            for user in pending_users:
-                auth_hrn = user.authority_hrn
 
-                request = {}
-                request['type'] = 'user'
-                request['id'] = 'TODO' # XXX in DB ?
-                request['timestamp'] = 'TODO' # XXX in DB ?
-                request['details'] = "%s %s <%s>" % (user.first_name, user.last_name, user.email)
+            requests = get_request_by_authority(queried_pending_authorities)
+            for request in requests:
+                auth_hrn = request['authority_hrn']
 
                 if auth_hrn in pi_my_authorities:
                     dest = ctx_my_authorities
@@ -1182,45 +1225,11 @@ class ValidatePendingView(TemplateView):
 
                 else:
                     continue
-                    
-                if not auth_hrn in dest:
-                    dest[auth_hrn] = []
-                print "auth_hrn [%s] was added %r" % (auth_hrn, request)
-                dest[auth_hrn].append(request) 
-
-            for slice in pending_slices:
-                auth_hrn = slice.authority_hrn
-                if not auth_hrn:
-                    auth_hrn = "ple.upmc" # XXX HARDCODED
-
-                request = {}
-                request['type'] = 'slice'
-                request['id'] = 'TODO' # XXX in DB ?
-                request['timestamp'] = 'TODO' # XXX in DB ?
-                request['details'] = "Number of nodes: %d -- Type of nodes: %s<br/>%s" % ('TODO', 'TODO', 'TODO') # XXX 
-                if auth_hrn in pi_my_authorities:
-                    dest = ctx_my_authorities
-
-                    # define the css class
-                    if auth_hrn in pi_credential_authorities:
-                        request['allowed'] = 'allowed'
-                    elif auth_hrn in pi_expired_credential_authorities:
-                        request['allowed'] = 'expired'
-                    else: # pi_no_credential_authorities
-                        request['allowed'] = 'denied'
-
-                elif auth_hrn in pi_delegation_authorities:
-                    dest = ctx_delegation_authorities
 
-                    if auth_hrn in pi_delegation_credential_authorities:
-                        request['allowed'] = 'allowed'
-                    else: # pi_delegation_expired_authorities
-                        request['allowed'] = 'expired'
-                    
                 if not auth_hrn in dest:
                     dest[auth_hrn] = []
                 dest[auth_hrn].append(request) 
-
+        
         context = super(ValidatePendingView, self).get_context_data(**kwargs)
         context['my_authorities']   = ctx_my_authorities
         context['delegation_authorities'] = ctx_delegation_authorities