Merge branch 'master' into scheduler
authorJordan Augé <jordan.auge@lip6.fr>
Fri, 29 Nov 2013 09:30:26 +0000 (10:30 +0100)
committerJordan Augé <jordan.auge@lip6.fr>
Fri, 29 Nov 2013 09:30:26 +0000 (10:30 +0100)
Conflicts:
plugins/googlemap/static/js/googlemap.js

20 files changed:
Makefile
apache/APACHE.notes [new file with mode: 0644]
apache/unfold.conf [moved from apache/myslice.conf with 66% similarity]
apache/unfold.wsgi [moved from myslice/wsgi.py with 100% similarity]
debian/deb-cheat-sheet [new file with mode: 0644]
debian/unfold.install
debian/unfold.postinst
myslice/settings.py
plugins/googlemap/static/js/googlemap.js
plugins/querytable/static/js/querytable.js
plugins/tabs/__init__.py
plugins/tabs/static/js/tabs.js
plugins/tabs/templates/tabs.html
portal/registrationview.py
portal/sliceview.py
setup.py
unfold/composite.py
unfold/static/js/plugin_helper.js
unfold/wsgi.py [new symlink]
unfold/wsgi.readme [new file with mode: 0644]

index 473e3d7..e3449bb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -148,7 +148,7 @@ ifeq (,$(MYSLICEBOX))
        @echo "  $(MAKE) MYSLICEBOX=debian04.pl.sophia.inria.fr "$@""
        @exit 1
 else
-       +$(RSYNC) ./apache/myslice.conf $(SSHURL)/$(INSTALLED_APACHE)/
+       +$(RSYNC) ./apache/unfold.conf $(SSHURL)/$(INSTALLED_APACHE)/
        +$(RSYNC) ./apache/unfold-init-ssl.sh $(SSHURL)/$(bindir)/
 endif
 
diff --git a/apache/APACHE.notes b/apache/APACHE.notes
new file mode 100644 (file)
index 0000000..7c77e4b
--- /dev/null
@@ -0,0 +1,29 @@
+The apache config as it ships in unfold.conf defines a port
+(currently 443) where SSL client-auth is enforced
+
+The idea being to have the browser prompting our user for a
+certificate - instead of leaving that optional, which we believe is
+something nobody will ever use if it's optional.
+
+A few notes and caveats must be outlined though below; see also unfold-init-ssl.sh about that
+
+* as of this writing quite a lot of what is below would be taken care
+  of by the packaging stuff once/if it works; 
+  the notes below are intended to help in this respect.
+
+* all the local material for this deployment gets into /etc/unfold/
+
+* I could not find a way to have client-auth without server auth;
+  this is totally weird, and stupid, but just so
+  so there is a need to install a (probably self-signed) cert 
+  and related key in 
+/etc/unfold/myslice.cert
+/etc/unfold/myslice.key
+  see init-ssl.sh for how to create these
+
+* Now the trusted roots - that we do need in our case - are expected in 
+/etc/unfold/trusted_roots
+ this of course is a user choice, e.g.:
+/etc/unfold/trusted_roots/plc.gid
+/etc/unfold/trusted_roots/ple.gid
+
similarity index 66%
rename from apache/myslice.conf
rename to apache/unfold.conf
index c994af3..ff3de7f 100644 (file)
@@ -1,9 +1,11 @@
-# xxx it might be smarter to install wsgi.py in some other location 
-# so we don't have to hard-wire these paths here
 <VirtualHost *:80>
-        WSGIScriptAlias / /usr/lib/python2.7/dist-packages/myslice/wsgi.py
-        <Directory /usr/share/unfold/myslice>
-        <Files wsgi.py>
+       WSGIDaemonProcess unfold processes=2 threads=25
+       WSGIProcessGroup  unfold
+       CustomLog ${APACHE_LOG_DIR}/unfold-access.log common
+       ErrorLog ${APACHE_LOG_DIR}/unfold-error.log
+        WSGIScriptAlias / /usr/share/unfold/apache/unfold.wsgi
+        <Directory /usr/share/unfold/apache/>
+        <Files unfold.wsgi>
         Order deny,allow
         Allow from all
         </Files>
 # this to be optional on that port
 
 <VirtualHost *:443>
-        WSGIScriptAlias / /usr/lib/python2.7/dist-packages/myslice/wsgi.py
-        <Directory /usr/share/unfold/apache>
-        <Files myslice.wsgi>
+       WSGIDaemonProcess unfold-ssl processes=2 threads=25
+       WSGIProcessGroup  unfold-ssl
+       CustomLog ${APACHE_LOG_DIR}/myslice-ssl-access.log common
+       ErrorLog ${APACHE_LOG_DIR}/myslice-ssl-error.log
+        WSGIScriptAlias / /usr/share/unfold/apache/unfold.wsgi
+        <Directory /usr/share/unfold/apache/>
+        <Files unfold.wsgi>
         Order deny,allow
         Allow from all
         </Files>
similarity index 100%
rename from myslice/wsgi.py
rename to apache/unfold.wsgi
diff --git a/debian/deb-cheat-sheet b/debian/deb-cheat-sheet
new file mode 100644 (file)
index 0000000..8524fb8
--- /dev/null
@@ -0,0 +1,18 @@
+--- search package name
+apt-cache search emacs
+--- install one package
+apt-get install emacs23-nox
+--- preparing upgrade (fetch and index latest versions of the repos)
+apt-get update 
+--- actually update one package
+apt-get upgrade myslice
+--- list packages
+dpkg -l
+dpkg -l '*foo*'
+--- list (files for) one package 
+dpkg -L myslice
+dpkg-deb -c myslice_0.2.4.lxc.2013.11.26_amd64.deb
+--- detailed info (e.g. to see complete version that is truncated in dpkg -l)
+dpkg -s myslice
+dpkg --info -c myslice_0.2.4.lxc.2013.11.26_amd64.deb
+
index 5bf0af9..9730d70 100644 (file)
@@ -8,5 +8,7 @@ usr/lib*/python*/dist-packages/myslice
 usr/lib*/python*/dist-packages/sample
 usr/share/unfold/static
 usr/share/unfold/templates
+apache/unfold.wsgi /usr/share/unfold/apache/
+apache/unfold.conf /etc/apache2/sites-available
 manage.py usr/share/unfold/
-apache/myslice.conf /etc/apache2/sites-available
+usr/bin/unfold-init-ssl.sh
index de85dd3..dd5c680 100644 (file)
@@ -1,10 +1,15 @@
 #!/bin/bash
 # if this requires a service to be running, add something like this
 # update-rc.d unfold defaults
+[ -d /var/unfold ] || mkdir /var/unfold
+chown -R www-data.www-data /var/unfold
+chmod -R 700 /var/unfold
+# upgrading from older packages -- temporary 
+[ -f /usr/share/unfold/myslice.sqlite3 ] && mv -f /usr/share/unfold/myslice.sqlite3 /var/unfold
+rm -f /etc/apache2/sites*/myslice.conf
+# upgrading end
 /usr/share/unfold/manage.py syncdb
 /usr/share/unfold/manage.py migrate
-chmod 777 /usr/share/unfold/myslice.sqlite3
-chmod 777 /usr/share/unfold
 a2dissite default
-a2ensite myslice.conf
+a2ensite unfold.conf
 service apache2 restart
index 4df98e2..c51010a 100644 (file)
@@ -1,4 +1,4 @@
-# Django settings for myslice project.
+# Django settings for unfold project.
 
 import os.path
 
@@ -19,17 +19,20 @@ except:
     import traceback
     traceback.print_exc()
 
-# find out DATAROOT, which is different from ROOT 
+# find out HTTPROOT, which is different from ROOT 
 # when deployed from a package
 # this code is run by collectstatic too, so we cannot
 # assume we have ./static present already
-DATAROOT="/usr/share/unfold"
+HTTPROOT="/usr/share/unfold"
+# the place to store local data, like e.g. the sqlite db
+DATAROOT="/var/unfold"
 # if not there, then we assume it's from a devel tree
-if not os.path.isdir (os.path.join(DATAROOT,"static")):
+if not os.path.isdir (os.path.join(HTTPROOT,"static")):
+    HTTPROOT=ROOT
     DATAROOT=ROOT
 
-if not os.path.isdir(ROOT): raise Exception,"Cannot find ROOT %s for myslice"%ROOT
-if not os.path.isdir(DATAROOT): raise Exception,"Cannot find DATAROOT %s for myslice"%DATAROOT
+if not os.path.isdir(ROOT): raise Exception,"Cannot find ROOT %s for unfold"%ROOT
+if not os.path.isdir(HTTPROOT): raise Exception,"Cannot find HTTPROOT %s for unfold"%HTTPROOT
 
 ####################
 ADMINS = (
@@ -49,7 +52,7 @@ EMAIL_USE_TLS = False
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': os.path.join(DATAROOT,'myslice.sqlite3'), # Or path to database file if using sqlite3.
+        'NAME': os.path.join(DATAROOT,'unfold.sqlite3'), # Or path to database file if using sqlite3.
         'USER': '',                      # Not used with sqlite3.
         'PASSWORD': '',                  # Not used with sqlite3.
         'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
@@ -93,7 +96,7 @@ MEDIA_URL = ''
 # Don't put anything in this directory yourself; store your static files
 # in apps' "static/" subdirectories and in STATICFILES_DIRS.
 # Example: "/home/media/media.lawrence.com/static/"
-STATIC_ROOT = os.path.join(DATAROOT,'static')
+STATIC_ROOT = os.path.join(HTTPROOT,'static')
 
 # URL prefix for static files.
 # Example: "http://media.lawrence.com/static/"
@@ -157,13 +160,13 @@ MIDDLEWARE_CLASSES = (
 ROOT_URLCONF = 'myslice.urls'
 
 # Python dotted path to the WSGI application used by Django's runserver.
-WSGI_APPLICATION = 'myslice.wsgi.application'
+WSGI_APPLICATION = 'unfold.wsgi.application'
 
 TEMPLATE_DIRS = (
     # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
     # Always use forward slashes, even on Windows.
     # Don't forget to use absolute paths, not relative paths.
-    os.path.join(DATAROOT,"templates"),
+    os.path.join(HTTPROOT,"templates"),
 )
 
 INSTALLED_APPS = (
index 23345a2..d600674 100644 (file)
@@ -44,6 +44,15 @@ googlemap_debug_detailed=false;
             // 
             this.key = (keys && keys.length == 1) ? keys[0] : null;
 
+           // xxx temporary hack
+           // as of nov. 28 2013 we have here this.key='urn', but in any place where
+           // the code tries to access record[this.key] the records only have
+           // keys=type,hrn,network_hrn,hostname
+           // so for now we force using hrn instead
+           // as soon as record have their primary key set this line can be removed
+           // see also same hack in querytable
+           this.key= (this.key == 'urn') ? 'hrn' : this.key;
+
             //// Setup query and record handlers 
             // this query is the one about the slice itself 
             // event related to this query will trigger callbacks like on_new_record
@@ -86,75 +95,79 @@ googlemap_debug_detailed=false;
             this.infowindow = new google.maps.InfoWindow();
         }, // initialize_map
 
-        // xxx probably not the right place
-        // The function accepts both records and their key 
-        record_hrn : function (record) {
-            var key_value;
-            switch (manifold.get_type(record)) {
+        // The function accepts both records and their id
+       // record.key points to the name of the primary key for this record
+       // typically this is 'urn'
+       record_id : function (input) {
+            var id;
+            switch (manifold.get_type(input)) {
             case TYPE_VALUE:
-                key_value = record;
+               id = input;
                 break;
             case TYPE_RECORD:
-                if ( ! this.key in record ) return;
-                key_value = record[this.key];
+               if ( ! this.key in input ) return;
+                id = input[this.key];
                 break;
             default:
-                throw "Not implemented";
+                throw "googlemap.record_id: not implemented";
                 break;
             }
-            // XXX BACKSLASHES original code was reading like this
-            //return this.escape_id(key_value).replace(/\\/g, '');
-            //  however this sequence removes backslashes from hrn's and as a result
-            // queryupdater was getting all mixed up
-            // querytable does publish hrn's with backslashes and that seems like the thing to do
-            return key_value;
-        },            
-
-        // return { marker: gmap_marker, ul : <ul DOM> }
-        create_marker_struct: function (object,lat,lon) {
-            // the DOM fragment
-            var dom = $("<p>").addClass("geo").append(object+"(s)");
-            var ul = $("<ul>").addClass("geo");
-            dom.append(ul);
-            // add a gmap marker to the mix
-            var marker = new google.maps.Marker({
-                position: new google.maps.LatLng(lat, lon),
+           return id;
+       },
+
+       // return { marker: gmap_marker, ul : <ul DOM> }
+       create_marker_struct: function (object,lat,lon) {
+           // the DOM fragment
+           var dom = $("<p>").addClass("geo").append(object+"(s)");
+           var ul = $("<ul>").addClass("geo");
+           dom.append(ul);
+           // add a gmap marker to the mix
+           var marker = new google.maps.Marker({
+               position: new google.maps.LatLng(lat, lon),
                 title: object,
                 // gmap can deal with a DOM element but not a jquery object
                 content: dom.get(0),
             }); 
-            return {marker:marker, ul:ul};
-        },
-
-        // add an entry in the marker <ul> tag for that record
-        // returns { checkbox : <input DOM> }
-        create_record_checkbox: function (record,ul,checked) {
-            var checkbox = $("<input>", {type:'checkbox', checked:checked, class:'geo'});
-            var hrn=this.record_hrn(record);
-            ul.append($("<li>").addClass("geo").append(checkbox).
-                      append($("<span>").addClass("geo").append(hrn)));
-            var googlemap=this;
-            // the callback for when a user clicks
-            // NOTE: this will *not* be called for changes done by program
-            checkbox.change( function (e) {
-                if (googlemap_debug) messages.debug("googlemap click handler checked= " + this.checked + " hrn=" + hrn);
-                manifold.raise_event (googlemap.options.query_uuid, 
-                                      this.checked ? SET_ADD : SET_REMOVED, hrn);
-            });
-            return checkbox;
-        },
-            
-        // retrieve DOM checkbox and make sure it is checked/unchecked
+           return {marker:marker, ul:ul};
+       },
+
+       // given an input <ul> element, this method inserts a <li> with embedded checkbox 
+       // for displaying/selecting the resource corresponding to the input record
+       // returns the created <input> element for further checkbox manipulation
+       create_record_checkbox: function (record,ul,checked) {
+           var checkbox = $("<input>", {type:'checkbox', checked:checked, class:'geo'});
+           var id=this.record_id(record);
+           // use hrn as far as possible for displaying
+           var label= ('hrn' in record) ? record.hrn : id;
+           ul.append($("<li>").addClass("geo").append(checkbox).
+                     append($("<span>").addClass("geo").append(label)));
+           var googlemap=this;
+           // the callback for when a user clicks
+           // NOTE: this will *not* be called for changes done by program
+           checkbox.change( function (e) {
+               manifold.raise_event (googlemap.options.query_uuid, this.checked ? SET_ADD : SET_REMOVED, id);
+           });
+           return checkbox;
+       },
+           
+       warning: function (record,message) {
+           try {messages.warning (message+" -- hostname="+record.hostname); }
+           catch (err) {messages.warning (message); }
+       },
+           
+       // retrieve DOM checkbox and make sure it is checked/unchecked
         set_checkbox: function(record, checked) {
-            var hrn=this.record_hrn (record);
-            if (! hrn) { 
-                try {messages.warning ("googlemap.set_checkbox: record has no hrn -- hostname="+record.hostname); }
-                catch (err) {messages.warning ("googlemap.set_checkbox: record has no hrn"); }
-                return; 
-            }
-            var checkbox_s = this.by_hrn [ hrn ];
-            if (! checkbox_s ) { messages.warning ("googlemap.set_checkbox: could not spot checkbox for hrn "+hrn); return; }
-            checkbox_s.checkbox.prop('checked',checked);
+           var id=this.record_id (record);
+           if (! id) { 
+               this.warning (record, "googlemap.set_checkbox: record has no id");
+               return; 
+           }
+           var checkbox = this.by_id [ id ];
+           if (! checkbox ) { 
+               this.warning (record, "googlemap.set_checkbox: checkbox not found");
+               return; 
+           }
+           checkbox.prop('checked',checked);
         }, // set_checkbox
 
         // this record is *in* the slice
@@ -167,36 +180,24 @@ googlemap_debug_detailed=false;
             var longitude=unfold.get_value(record['longitude']);
             var lat_lon = latitude + longitude;
 
-                // check if we've seen anything at that place already
-                // xxx might make sense to allow for some fuzziness, 
-                // i.e. consider 2 places equal if not further away than 300m or so...
-                var marker_s = this.by_lat_lon [lat_lon];
-                if ( marker_s == null ) {
-                        marker_s = this.create_marker_struct (this.object, latitude, longitude);
-                        this.by_lat_lon [ lat_lon ] = marker_s;
-                        this.arm_marker(marker_s.marker, this.map);
-                }
-            
-                // now add a line for this resource in the marker
-                // xxx should compute checked here ?
-                // this is where the checkbox will be appended
-                var ul=marker_s.ul;
-                var checkbox = this.create_record_checkbox (record, ul, false);
-                if ( ! this.key in record ) return;
-            var key_value = record[this.key];
-                // see XXX BACKSLASHES 
-                //var hrn = this.escape_id(key_value).replace(/\\/g, '');
-                var hrn = key_value;
-            this.by_hrn[hrn] = {
-                        checkbox: checkbox,
-                        // xxx Thierry sept 2013
-                        // xxx actually we might have just used a domid-based scheme instead of the hash
-                        // since at this point we only need to retrieve the checkbox from an hrn
-                        // but I was not sure enough that extra needs would not show up so I kept this in place
-                        // xxx not sure these are actually useful :
-                value: key_value,
-                record: record,
-            }
+           // check if we've seen anything at that place already
+           // xxx might make sense to allow for some fuzziness, 
+           // i.e. consider 2 places equal if not further away than 300m or so...
+           var marker_s = this.by_lat_lon [lat_lon];
+           if ( marker_s == null ) {
+               marker_s = this.create_marker_struct (this.object, latitude, longitude);
+               this.by_lat_lon [ lat_lon ] = marker_s;
+               this.arm_marker(marker_s.marker, this.map);
+           }
+           
+           // now add a line for this resource in the marker
+           // xxx should compute checked here ?
+           // this is where the checkbox will be appended
+           var ul=marker_s.ul;
+           var checkbox = this.create_record_checkbox (record, ul, false);
+           var id=this.record_id(record);
+           // used to keep a dict here, but only checkbox is required
+            this.by_id[id] = checkbox;
         }, // new_record
 
         arm_marker: function(marker, map) {
index e0ac3fd..12232c8 100644 (file)
             var keys = manifold.metadata.get_key(this.method);
             this.key = (keys && keys.length == 1) ? keys[0] : null;
 
+           // xxx temporary hack
+           // as of nov. 28 2013 we have here this.key='urn', but in any place where
+           // the code tries to access record[this.key] the records only have
+           // keys=type,hrn,network_hrn,hostname
+           // so for now we force using hrn instead
+           // as soon as record have their primary key set this line can be removed
+           // see also same hack in googlemap
+           this.key= (this.key == 'urn') ? 'hrn' : this.key;
+
             /* Setup query and record handlers */
             this.listen_query(options.query_uuid);
             this.listen_query(options.query_all_uuid, 'all');
             /* Default: checked = true */
             if (checked === undefined) checked = true;
 
-            var key_value;
+            var id;
             /* The function accepts both records and their key */
             switch (manifold.get_type(record)) {
-                case TYPE_VALUE:
-                    key_value = record;
-                    break;
-                case TYPE_RECORD:
-                    /* XXX Test the key before ? */
-                    key_value = record[this.key];
-                    break;
-                default:
-                    throw "Not implemented";
-                    break;
+            case TYPE_VALUE:
+                id = record;
+                break;
+            case TYPE_RECORD:
+                /* XXX Test the key before ? */
+                id = record[this.key];
+                break;
+            default:
+                throw "Not implemented";
+                break;
             }
 
 
-           if (key_value === undefined) {
-               messages.warning("querytable.set_checkbox has no value to figure which line to tick");
+           if (id === undefined) {
+               messages.warning("querytable.set_checkbox record has no id to figure which line to tick");
                return;
            }
-            var checkbox_id = this.flat_id(this.id('checkbox', key_value));
+            var checkbox_id = this.flat_id(this.id('checkbox', id));
             // function escape_id(myid) is defined in portal/static/js/common.functions.js
             checkbox_id = escape_id(checkbox_id);
             // using dataTables's $ to search also in nodes that are not currently displayed
index d1b9bda..4bab62d 100644 (file)
@@ -1,7 +1,24 @@
 from unfold.composite import Composite
 
+# although the persistent_active feature originally targetted any kind of 
+# composite widget, the current implementation clearly 
+# works only with Tabs, as the javascript function for enabling a given tab
+# cannot just set the 'active' class on some DOM element, but is required
+# by bootstrap to call the tab() javascript method
+
 class Tabs (Composite):
     
+    """A composite plugin for arranging sons as selectable tabs
+    
+persistent_active: if set to True, enables preserving 
+the domid for the active tab, so that a given page
+will re-open with the same active tab
+"""
+
+    def __init__ (self, persistent_active=False, *args, **kwds):
+        Composite.__init__ (self, *args, **kwds)
+        self.persistent_active=persistent_active
+        
     def requirements (self):
         return { 'js_files'     : ['js/tabs.js', 'js/bootstrap.js'],
                  'css_files'    : ['css/bootstrap.css', 'css/tabs.css', ] 
@@ -10,6 +27,11 @@ class Tabs (Composite):
     def template_file (self):
         return "tabs.html"
 
+    def template_env (self, request):
+        inherited=Composite.template_env(self,request)
+        inherited.update({'persistent_active':self.persistent_active})
+        return inherited
+
     # see Composite.py for the details of template_env, that exposes global
     # 'sons' as a list of sons with each a set of a few attributes
     def json_settings_list (self):
index 75ce452..bfd568a 100644 (file)
@@ -1,3 +1,71 @@
+// storing tabs active component in localStorage
+//
+// based on plugin_helper.js, extended to store the domid of an active tab
+//
+
+var tabs_helper = {
+
+    debug:false,
+
+    ////////// use local storage to remember the active son
+    // xxx this is a bit fragile in that we assume the elements that need to have the 'active' class
+    // are direct sons of the .persistent-active elements
+    // it would be simple to overcome this weakness by requiring the template to set
+    // .persistent-active-item on these elements active-able instead 
+    store_active_domid : function (domid,active_domid) {
+       var key='active.'+domid;
+       if (tabs_helper.debug) messages.debug("storing for domid " + domid + " (key=" + key + ") active_domid=" + active_domid);
+       $.localStorage.setItem(key,active_domid);
+    },
+    // restore last status
+    retrieve_last_active_domid : function (domid) {
+       var key='active.'+domid;
+       // don't do anything if nothing stored
+       var retrieved=$.localStorage.getItem(key);
+       // set default to undefined
+       if (retrieved==null) retrieved=undefined;
+       if (tabs_helper.debug) messages.debug ("retrieved active status for " + domid + " (key=" + key + ") -> " + retrieved);
+       return retrieved;
+    },
+    // tabs_helper.set_active_domid("tabs-resources","tab-resources-list")
+    set_active_domid : function (domid,active_domid) {
+       if ( ! active_domid ) return;
+       if (tabs_helper.debug) messages.debug ("setting active for " + domid + " to active_domid=" + active_domid);
+       // build something like "#uldomid a[href='#active_domid']"
+       var selector="#"+domid+" a[href='#"+active_domid+"']";
+       console.log("selector="+selector);
+       $(selector).tab('show');
+    },
+    set_from_saved_active_domid : function (domid) {
+       var active_domid=tabs_helper.retrieve_last_active_domid (domid);
+       if (tabs_helper.debug) messages.debug("restoring active domid for domid " + domid + " -> " + active_domid);
+       tabs_helper.set_active_domid (domid,active_domid);
+    },
+
+    init_all_tabs : function () {
+       ////////// active
+       $('.persistent-active').each(function() {
+           var domid=this.id;
+           tabs_helper.set_from_saved_active_domid(domid);
+       });
+       // on all the children of the persistent-active element
+       $('.persistent-active>*').on('shown.bs.tab', function (e) {
+           var active_domid=e.target;
+           // at this point we have something like 
+           // http://localhost:8080/portal/slice/ple.inria.f14#tab-resources-list
+           active_domid=active_domid.hash.replace("#","");
+           // find out the domid, which is for the nearest ancestor with class
+           // persistent-active
+           var domid=$(e.target).closest(".persistent-active").attr('id');
+           tabs_helper.store_active_domid(domid,active_domid);
+           console.log("CLICKED domid="+domid+" active_domid="+active_domid);
+       });
+    },
+
+} // global var tabs_helper
+
+$(document).ready(tabs_helper.init_all_tabs);
+
 (function($){
     $.fn.Tabs = function( method ) {
         // In Bootstrap 3, we need 'shown.bs.tab' instead of 'shown'
index 298dec9..60031f6 100644 (file)
@@ -1,4 +1,4 @@
-<ul class="nav nav-tabs" id='tabs-{{ domid }}'>
+<ul class="nav nav-tabs{%if persistent_active%} persistent-active{%endif%}" id='tabs-{{ domid }}'>
 {% for son in sons %}
 <li{% if son.is_active %} class='active'{% endif %}> <a href="#tab-{{ son.domid }}" data-toggle="tab">{{ son.title }}</a> </li>
 {% endfor %}
index b9f35ff..8f7032b 100644 (file)
@@ -37,13 +37,13 @@ class RegistrationView (FreeAccessView):
         #onelab_enabled_query = Query.get('local:platform').filter_by('platform', '==', 'ple').filter_by('disabled', '==', 'False')
         #onelab_enabled = not not execute_admin_query(request, onelab_enabled_query)
         #if onelab_enabled:
-        #if True:
-        #    print "ONELAB ENABLED"
-        #    authorities_query = Query.get('ple:authority').select('name', 'authority_hrn').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc', 'ple.ibbtple', 'ple.nitos'])
-        #else:
-        #    print "FIREXP ENABLED"
+        if True:
+            print "ONELAB ENABLED"
+            authorities_query = Query.get('ple:authority').select('name', 'authority_hrn').filter_by('authority_hrn', 'included', ['ple.inria', 'ple.upmc', 'ple.ibbtple', 'ple.nitos'])
+        else:
+            print "FIREXP ENABLED"
 
-        #authorities = execute_admin_query(request, authorities_query)
+        authorities = execute_admin_query(request, authorities_query)
         # xxx tocheck - if authorities is empty, it's no use anyway
         # (users won't be able to validate the form anyway)
 
@@ -53,6 +53,9 @@ class RegistrationView (FreeAccessView):
 
         print 'registration view, method',method
 
+        user_query  = Query().get('local:user').select('user_id','email')
+        user_details = execute_admin_query(self.request, user_query)
+
         if method == 'POST':
             # We shall use a form here
 
@@ -75,7 +78,10 @@ class RegistrationView (FreeAccessView):
                 errors.append('Last Name may contain only letters, numbers, spaces and @/./+/-/_ characters.')
             # XXX validate authority hrn !!
             if PendingUser.objects.filter(email__iexact=reg_email):
-                errors.append('Email already registered.Please provide a new email address.')
+                errors.append('Email is pending for validation. Please provide a new email address.')
+            for user_detail in user_details:
+                if user_detail['email']==reg_email:
+                    errors.append('Email already exists in Manifold. Please provide a new email address.')
 
 # XXX TODO: Factorize with portal/accountview.py
             if 'generate' in request.POST['question']:
@@ -132,10 +138,15 @@ class RegistrationView (FreeAccessView):
                     keypair       = keypair,
                 )
                 b.save()
-                #saving to manifold
-                config = '{"firstname":'+ reg_fname + ', "lastname":'+ reg_lname + ', "authority":"'+ reg_auth + '"}'
+                #creating user to manifold local:user
+                config = '{"firstname":"'+ reg_fname + '", "lastname":"'+ reg_lname + '", "authority":"'+ reg_auth + '"}'
                 user_params = {'email': reg_email, 'password': request.POST['password'], 'config': config}
-                manifold_add_user(request,user_params) 
+                manifold_add_user(request,user_params)
+                #creating local:account in manifold
+                user_id = user_detail['user_id']+1 # the user_id for the newly created user in local:user
+                user_params = {'platform_id': 5, 'user_id': user_id, 'auth_type': auth_type, 'config': keypair}
+                manifold_add_account(request,user_params)
                 # Send email
                 ctx = {
                     'first_name'    : reg_fname, 
@@ -153,21 +164,7 @@ class RegistrationView (FreeAccessView):
 
                 msg = render_to_string('user_request_email.txt', ctx)
                 send_mail("Onelab New User request for %s submitted"%reg_email, msg, reg_email, recipients)
-
-                #return render(request, 'user_register_complete.html')
-            
-            #creating local:account in manifold
-            user_query  = Query().get('local:user').select('user_id','email')
-            user_details = execute_admin_query(self.request, user_query)
-            
-            for user_detail in user_details:
-                if user_detail['email']==reg_email:
-                    user_id = user_detail['user_id']
-                    #print "test"
-                    #print user_id
-            user_params = {'platform_id': 5, 'user_id': user_id, 'auth_type': auth_type, 'config': keypair}    
-            manifold_add_account(request,user_params)
-            return render(request, 'user_register_complete.html') 
+                return render(request, 'user_register_complete.html') 
 
         template_env = {
           'topmenu_items': topmenu_items('Register', request),
@@ -178,7 +175,7 @@ class RegistrationView (FreeAccessView):
           'authority_hrn': request.POST.get('authority_hrn', ''),
           'email': request.POST.get('email', ''),
           'password': request.POST.get('password', ''),           
-          #'authorities': authorities,
+          'authorities': authorities,
           }
         template_env.update(page.prelude_env ())
         return render(request, 'registration_view.html',template_env)
index 8b0c552..42cc8ba 100644 (file)
@@ -211,6 +211,7 @@ class SliceView (LoginRequiredAutoLogoutView):
                                        #resources_as_3dmap,
                                        resources_as_list_area, ],
                                 active_domid = 'resources-map',
+                                persistent_active=True,
                                 )
         main_stack.insert (resources_area)
 
index 99b077d..e7ab6a5 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 #
-# Setup script for myslice
+# Setup script for myslice+unfold
 #
 # Thierry Parmentelat <thierry.parmentelat@inria.fr>
 # INRIA (c) 2013
@@ -21,5 +21,5 @@ setup(packages = packages,
         ( '/usr/share/unfold/static/img', glob ('static/img/*')),
         ( '/usr/share/unfold/static/fonts', glob ('static/fonts/*')),
         ( '/usr/share/unfold/templates', glob ('templates/*')),
-        ( 'apache', [ 'apache/myslice.conf' ]),
+        ( 'apache', [ 'apache/unfold.conf', 'apache/unfold.wsgi' ]),
         ])
index 16e28f3..f3d42fc 100644 (file)
@@ -1,13 +1,14 @@
 from unfold.plugin import Plugin
 
-# this is a simple base class for plugins that contain/arrange a set of other plugins
-# sons is expected to be a list of the contained plugins, and 
-# active_domid is the domid for the one son that should be displayed as active
-# some subclasses of Composite, like e.g. Tabs, will not behave as expected 
-# if a valid active_domid is not provided
-
 class Composite (Plugin):
 
+    """a simple base class for plugins that contain/arrange a set of other plugins
+sons is expected to be a list of the contained plugins, and 
+active_domid is the domid for the one son that should be displayed as active
+some subclasses of Composite, like e.g. Tabs, will not behave as expected 
+if a valid active_domid is not provided
+"""
+
     def __init__ (self, sons=None, active_domid=None, *args, **kwds):
         Plugin.__init__ (self, *args, **kwds)
         self.sons= sons if sons else []
index d15e7a7..7f81a3c 100644 (file)
@@ -1,18 +1,19 @@
 //
 // storing toggle's status in localStorage
 // NOTE that localStorage only stores strings, so true becomes "true"
+//
 var plugin_helper = {
 
     debug:false,
 
     ////////// use local storage to remember open/closed toggles
-    store_status : function (domid,status) {
+    store_toggle_status : function (domid,status) {
        var key='toggle.'+domid;
        if (plugin_helper.debug) messages.debug("storing toggle status " + status + " for " + domid + " key=" + key);
        $.localStorage.setItem(key,status);
     },
     // restore last status
-    retrieve_last_status : function (domid) {
+    retrieve_last_toggle_status : function (domid) {
        var key='toggle.'+domid;
        // don't do anything if nothing stored
        var retrieved=$.localStorage.getItem(key);
@@ -27,20 +28,22 @@ var plugin_helper = {
        var hidebtn=$('#hide-'+domid);
        if (status=="true")     { plugindiv.slideDown(); hidebtn.show(); showbtn.hide(); }
        else                    { plugindiv.slideUp();   hidebtn.hide(); showbtn.show(); }
-       plugin_helper.store_status(domid,status);
+       plugin_helper.store_toggle_status(domid,status);
     },
-    set_from_saved_status : function (domid) {
-       var previous_status=plugin_helper.retrieve_last_status (domid);
+    set_from_saved_toggle_status : function (domid) {
+       var previous_status=plugin_helper.retrieve_last_toggle_status (domid);
        if (plugin_helper.debug) messages.debug("restoring initial status for domid " + domid + " -> " + previous_status);
        plugin_helper.set_toggle_status (domid,previous_status);
     },
+
+
     // triggered upon $(document).ready
     init_all_plugins : function() {
        // plugins marked as persistent start with all 3 parts turned off
        // let us first make sure the right parts are turned on 
        $('.persistent-toggle').each(function() {
            var domid=this.id.replace('complete-','');
-           plugin_helper.set_from_saved_status(domid);
+           plugin_helper.set_from_saved_toggle_status(domid);
        });
        // program the hide buttons so they do the right thing
        $('.plugin-hide').each(function() {
@@ -57,7 +60,7 @@ var plugin_helper = {
        // arm tooltips
        $('.plugin-tooltip').each(function(){ $(this).tooltip({'selector':'','placement':'right'}); });
     },
-} // global var plugin
+} // global var plugin_helper
 
 /* upon document completion, we locate all the hide and show areas, 
  * and configure their behaviour 
diff --git a/unfold/wsgi.py b/unfold/wsgi.py
new file mode 120000 (symlink)
index 0000000..579c87b
--- /dev/null
@@ -0,0 +1 @@
+../apache/unfold.wsgi
\ No newline at end of file
diff --git a/unfold/wsgi.readme b/unfold/wsgi.readme
new file mode 100644 (file)
index 0000000..7c287ed
--- /dev/null
@@ -0,0 +1,9 @@
+not quite sure where to store the wsgi application file in the codebase
+it used to be in unfold/wsgi.py but that looked a bit awkward
+one of the reasons for that is, the apache/ config (apache/unfold.conf)
+needs to reference the installed wsgi file, so we'd rather not have this
+installed in the python library where names are not known in advance
+--
+So at some point it was decided to have it moved with the apache/ config
+The symlink here is only for the django development server to find it at run time
+see settings.py and the definition of WSGI_APPLICATION