From: Jordan Augé Date: Fri, 29 Nov 2013 09:30:26 +0000 (+0100) Subject: Merge branch 'master' into scheduler X-Git-Tag: myslice-0.3-0~103 X-Git-Url: http://git.onelab.eu/?p=myslice.git;a=commitdiff_plain;h=03839166e30819c37290f33056f44aaee1583bfc;hp=371ac3cd748e44a973ec81eebc3d2ac69c34ccf1 Merge branch 'master' into scheduler Conflicts: plugins/googlemap/static/js/googlemap.js --- diff --git a/Makefile b/Makefile index 473e3d76..e3449bbd 100644 --- 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 index 00000000..7c77e4bd --- /dev/null +++ b/apache/APACHE.notes @@ -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 + diff --git a/apache/myslice.conf b/apache/unfold.conf similarity index 66% rename from apache/myslice.conf rename to apache/unfold.conf index c994af36..ff3de7f0 100644 --- a/apache/myslice.conf +++ b/apache/unfold.conf @@ -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 - WSGIScriptAlias / /usr/lib/python2.7/dist-packages/myslice/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 + + Order deny,allow Allow from all @@ -23,9 +25,13 @@ # this to be optional on that port - WSGIScriptAlias / /usr/lib/python2.7/dist-packages/myslice/wsgi.py - - + 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 + + Order deny,allow Allow from all diff --git a/myslice/wsgi.py b/apache/unfold.wsgi 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 index 00000000..8524fb80 --- /dev/null +++ b/debian/deb-cheat-sheet @@ -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 + diff --git a/debian/unfold.install b/debian/unfold.install index 5bf0af9a..9730d70c 100644 --- a/debian/unfold.install +++ b/debian/unfold.install @@ -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 diff --git a/debian/unfold.postinst b/debian/unfold.postinst index de85dd31..dd5c6805 100644 --- a/debian/unfold.postinst +++ b/debian/unfold.postinst @@ -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 diff --git a/myslice/settings.py b/myslice/settings.py index 4df98e2e..c51010a4 100644 --- a/myslice/settings.py +++ b/myslice/settings.py @@ -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 = ( diff --git a/plugins/googlemap/static/js/googlemap.js b/plugins/googlemap/static/js/googlemap.js index 23345a27..d6006744 100644 --- a/plugins/googlemap/static/js/googlemap.js +++ b/plugins/googlemap/static/js/googlemap.js @@ -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 :
    } - create_marker_struct: function (object,lat,lon) { - // the DOM fragment - var dom = $("

    ").addClass("geo").append(object+"(s)"); - var 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 :
        } + create_marker_struct: function (object,lat,lon) { + // the DOM fragment + var dom = $("

        ").addClass("geo").append(object+"(s)"); + var 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
            tag for that record - // returns { checkbox : } - create_record_checkbox: function (record,ul,checked) { - var checkbox = $("", {type:'checkbox', checked:checked, class:'geo'}); - var hrn=this.record_hrn(record); - ul.append($("
          • ").addClass("geo").append(checkbox). - append($("").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
              element, this method inserts a
            • with embedded checkbox + // for displaying/selecting the resource corresponding to the input record + // returns the created element for further checkbox manipulation + create_record_checkbox: function (record,ul,checked) { + var checkbox = $("", {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($("
            • ").addClass("geo").append(checkbox). + append($("").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) { diff --git a/plugins/querytable/static/js/querytable.js b/plugins/querytable/static/js/querytable.js index e0ac3fd0..12232c8e 100644 --- a/plugins/querytable/static/js/querytable.js +++ b/plugins/querytable/static/js/querytable.js @@ -44,6 +44,15 @@ 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'); @@ -250,27 +259,27 @@ /* 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 diff --git a/plugins/tabs/__init__.py b/plugins/tabs/__init__.py index d1b9bda3..4bab62d9 100644 --- a/plugins/tabs/__init__.py +++ b/plugins/tabs/__init__.py @@ -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): diff --git a/plugins/tabs/static/js/tabs.js b/plugins/tabs/static/js/tabs.js index 75ce452a..bfd568a9 100644 --- a/plugins/tabs/static/js/tabs.js +++ b/plugins/tabs/static/js/tabs.js @@ -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' diff --git a/plugins/tabs/templates/tabs.html b/plugins/tabs/templates/tabs.html index 298dec92..60031f63 100644 --- a/plugins/tabs/templates/tabs.html +++ b/plugins/tabs/templates/tabs.html @@ -1,4 +1,4 @@ -