Merge branch 'master' of ssh://git.onelab.eu/git/myslice
authorLoic Baron <loic.baron@lip6.fr>
Fri, 13 Dec 2013 17:38:13 +0000 (18:38 +0100)
committerLoic Baron <loic.baron@lip6.fr>
Fri, 13 Dec 2013 17:38:13 +0000 (18:38 +0100)
Conflicts:
manifold/manifoldapi.py

15 files changed:
manifold/manifoldapi.py
manifold/static/js/plugin.js
plugins/googlemap/__init__.py
plugins/googlemap/static/js/googlemap.js
plugins/querygrid/static/js/querygrid.js
plugins/querytable/__init__.py
plugins/querytable/static/js/querytable.js
plugins/queryupdater/static/js/queryupdater.js
portal/accountview.py
portal/sliceview.py
portal/static/css/account_view.css
portal/templates/account-view.html
trash/simplegridview.py
trash/simpletableview.py [new file with mode: 0644]
trash/urls.py

index f8c33f9..6639d48 100644 (file)
@@ -132,6 +132,9 @@ def _execute_query(request, query, manifold_api_session_auth):
     print "-"*80
     result = manifold_api.forward(query.to_dict())
     if result['code'] == 2:
+        # this is gross; at the very least we need to logout() 
+        # but most importantly there is a need to refine that test, since 
+        # code==2 does not necessarily mean an expired session
         # XXX only if we know it is the issue
         del request.session['manifold']
         # Flush django session
index e278129..41d8e8b 100644 (file)
@@ -192,18 +192,21 @@ var Plugin = Class.extend({
         return key_field + manifold.separator + this.escape_id(value).replace(/\\/g, '');
     },
 
-    // we do not need to carry around all the nonsense about backslashing dots in hrns
-    // likewise, DOM ids do not like to have dots in them
-    // because "#foo.bar" matches an elem named foo with class bar - not an id that is foo.bar
-    // so this method gives you a valid DOMID but that cannot be 'reversed' back to retrieve an hrn or the like
-    // e.g. 
-    // input=input="ple.aluiple.host147-82-static\\.93-94-b\\.business\\.telecomitalia\\.it"
-    // > "ple.aluiple.host147-82-static\.93-94-b\.business\.telecomitalia\.it"
-    // flat_id(input)
-    // "ple-aluiple-host147-82-static-93-94-b-business-telecomitalia-it"
-    flat_id : function (id_in) {
-       return id_in.replace(/\\\./g,"-").replace(/\\/g,"-").replace(/\./g,"-");
-    },
+    // NOTE
+    // at some point in time we used to have a helper function named 'flat_id' here
+    // the goals was to sort of normalize id's but it turned out we can get rid of that
+    // in a nutshell, we would have an id (can be urn, hrn, whatever) and 
+    // we want to be able to retrieve a DOM element based on that (e.g. a checkbox)
+    // so we did something like <tag id="some-id-that-comes-from-the-db">
+    // and then $("#some-id-that-comes-from-the-db")
+    // however the syntax for that selector prevents from using some characters in id
+    // and so for some of our ids this won't work
+    // instead of 'flattening' we now do this instead
+    // <tag some_id="then!we:can+use.what$we!want">
+    // and to retrieve it
+    // $("[some_id='then!we:can+use.what$we!want']")
+    // which thanks to the quotes, works; and you can use this with id as well in fact
+    // of course if now we have quotes in the id it's going to squeak, but well..
 
     // escape (read: backslashes) some meta-chars in input
     escape_id: function(id) {
index 8ceaec6..583b8dd 100644 (file)
@@ -44,4 +44,8 @@ class GoogleMap (Plugin):
         return reqs
 
     # the list of things passed to the js plugin
-    def json_settings_list (self): return ['plugin_uuid','query_uuid', 'query_all_uuid', 'latitude', 'longitude', 'zoom', ]
+    def json_settings_list (self): 
+        return [ 'plugin_uuid', 'query_uuid', 'query_all_uuid',
+                 'init_key',
+                 'latitude', 'longitude', 'zoom', 
+                 ]
index c288830..764f1b4 100644 (file)
@@ -8,13 +8,16 @@
  * - infowindow is not properly reopened when the maps does not have the focus
  */
 
-// events that happen in the once-per-view range
-googlemap_debug=false;
-// more on a on-per-record basis
-googlemap_debug_detailed=false;
-
 (function($){
 
+    // events that happen in the once-per-view range
+    var debug=false;
+    debug=true;
+
+    // more on a on-per-record basis
+    var debug_deep=false;
+    // debug_deep=true;
+
     var GoogleMap = Plugin.extend({
 
         init: function(options, element) {
@@ -27,10 +30,13 @@ googlemap_debug_detailed=false;
             this.in_set_backlog = [];
 
             // we keep a couple of global hashes
-               // lat_lon --> { marker, <ul> }
-               // id --> { <li>, <input> }
-               this.by_lat_lon = {};
-               this.by_id = {};
+           // lat_lon --> { marker, <ul> }
+           // id --> { <li>, <input> }
+           this.by_lat_lon = {};
+           // locating checkboxes by DOM selectors might be abstruse, as we cannot safely assume 
+           // all the items will belong under the toplevel <div>
+           this.by_id = {};
+           this.by_init_id = {};
 
             /* XXX Events */
             this.elmt().on('show', this, this.on_show);
@@ -40,24 +46,17 @@ googlemap_debug_detailed=false;
             var query = manifold.query_store.find_analyzed_query(this.options.query_uuid);
             this.object = query.object;
 
-        //    var keys = manifold.metadata.get_key(this.object);
+           // see querytable.js for an explanation
+           var keys = manifold.metadata.get_key(this.object);
+           this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
            // 
-        //    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;
-        this.key = (this.options.id_key);
-        if (typeof(this.key)=='undefined' || (this.key).startsWith("unknown")) {
-            // if not specified by caller, decide from metadata
-            var keys = manifold.metadata.get_key(this.object);
-            this.key = (keys && keys.length == 1) ? keys[0] : null;
-        }
+           this.init_key = this.options.init_key;
+           // have init_key default to canonical_key
+           this.init_key = this.init_key || this.canonical_key;
+           // sanity check
+           if ( ! this.init_key ) messages.warning ("QueryTable : cannot find init_key");
+           if ( ! this.canonical_key ) messages.warning ("QueryTable : cannot find canonical_key");
+           if (debug) messages.debug("googlemap: canonical_key="+this.canonical_key+" init_key="+this.init_key);
 
             //// Setup query and record handlers 
            // this query is the one about the slice itself 
@@ -74,7 +73,7 @@ googlemap_debug_detailed=false;
         /* PLUGIN EVENTS */
 
         on_show: function(e) {
-           if (googlemap_debug) messages.debug("googlemap.on_show");
+           if (debug) messages.debug("googlemap.on_show");
             var googlemap = e.data;
             google.maps.event.trigger(googlemap.map, 'resize');
         }, // on_show
@@ -97,31 +96,11 @@ googlemap_debug_detailed=false;
            
             var domid = this.options.plugin_uuid + '--' + 'googlemap';
                var elmt = document.getElementById(domid);
-               if (googlemap_debug) messages.debug("gmap.initialize_map based on  domid=" + domid + " elmt=" + elmt);
+               if (debug) messages.debug("gmap.initialize_map based on  domid=" + domid + " elmt=" + elmt);
             this.map = new google.maps.Map(elmt, myOptions);
             this.infowindow = new google.maps.InfoWindow();
         }, // initialize_map
 
-        // 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:
-               id = input;
-                break;
-            case TYPE_RECORD:
-               if ( ! this.key in input ) return;
-                id = input[this.key];
-                break;
-            default:
-                throw "googlemap.record_id: not implemented";
-                break;
-            }
-           return id;
-       },
-
        // return { marker: gmap_marker, ul : <ul DOM> }
        create_marker_struct: function (object,lat,lon) {
            // the DOM fragment
@@ -145,17 +124,21 @@ googlemap_debug_detailed=false;
        // 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;
-           var label= (this.key in record) ? record[this.key] : id;
+           var id=record[this.canonical_key];
+           var init_id=record[this.init_key];
+           // xxx use init_key to find out label - or should we explicitly accept an incoming label_key ?
+           var label=init_id;
            ul.append($("<li>").addClass("geo").append(checkbox).
                      append($("<span>").addClass("geo").append(label)));
-           var googlemap=this;
+           // hash by id and by init_id 
+           this.by_id[id]=checkbox;
+            this.by_init_id[init_id] = checkbox;
+           //
            // the callback for when a user clicks
            // NOTE: this will *not* be called for changes done by program
+           var self=this;
            checkbox.change( function (e) {
-               manifold.raise_event (googlemap.options.query_uuid, this.checked ? SET_ADD : SET_REMOVED, id);
+               manifold.raise_event (self.options.query_uuid, this.checked ? SET_ADD : SET_REMOVED, id);
            });
            return checkbox;
        },
@@ -166,23 +149,22 @@ googlemap_debug_detailed=false;
        },
            
        // retrieve DOM checkbox and make sure it is checked/unchecked
-    set_checkbox: function(record, checked) {
-           var id=this.record_id (record);
-           if (! id) { 
-               this.warning (record, "googlemap.set_checkbox: record has no id");
-               return; 
-           }
+       set_checkbox_from_record: function(record, checked) {
+           var init_id=record[this.init_key];
+           var checkbox = this.by_init_id [ init_id ];
+           if (checkbox) checkbox.prop('checked',checked);
+           else this.warning(record, "googlemap.set_checkbox_from_record - not found "+init_id);
+       }, 
+
+       set_checkbox_from_data: function(id, checked) {
            var checkbox = this.by_id [ id ];
-           if (! checkbox ) { 
-               this.warning (record, "googlemap.set_checkbox: checkbox not found");
-               return; 
-           }
-           checkbox.prop('checked',checked);
-     }, // set_checkbox
+           if (checkbox) checkbox.prop('checked',checked);
+           else messages.warning("googlemap.set_checkbox_from_data - id not found "+id);
+       }, 
 
        // this record is *in* the slice
         new_record: function(record) {
-               if (googlemap_debug_detailed) messages.debug ("new_record");
+           if (debug_deep) messages.debug ("googlemap.new_record");
             if (!(record['latitude'])) return false;
            
             // get the coordinates
@@ -205,13 +187,10 @@ googlemap_debug_detailed=false;
            // 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) {
-           if (googlemap_debug_detailed) messages.debug ("arm_marker content="+marker.content);
+           if (debug_deep) messages.debug ("arm_marker content="+marker.content);
             var googlemap = this;
             google.maps.event.addListener(marker, 'click', function () {
                 googlemap.infowindow.close();
@@ -224,27 +203,27 @@ googlemap_debug_detailed=false;
 
         /*************************** RECORD HANDLER ***************************/
         on_new_record: function(record) {
-           if (googlemap_debug_detailed) messages.debug("on_new_record");
+           if (debug_deep) messages.debug("on_new_record");
             if (this.received_all)
                 // update checkbox for record
-                this.set_checkbox(record, true);
+                this.set_checkbox_from_record(record, true);
             else
                 // store for later update of checkboxes
                 this.in_set_backlog.push(record);
         },
 
         on_clear_records: function(record) {
-           if (googlemap_debug_detailed) messages.debug("on_clear_records");
+           if (debug_deep) messages.debug("on_clear_records");
         },
 
         // Could be the default in parent
         on_query_in_progress: function() {
-           if (googlemap_debug) messages.debug("on_query_in_progress (spinning)");
+           if (debug) messages.debug("on_query_in_progress (spinning)");
             this.spin();
         },
 
         on_query_done: function() {
-               if (googlemap_debug) messages.debug("on_query_done");       
+               if (debug) messages.debug("on_query_done");         
             if (this.received_all) {
                 this.unspin();
                }
@@ -252,15 +231,15 @@ googlemap_debug_detailed=false;
         },
 
         on_field_state_changed: function(data) {
-           if (googlemap_debug_detailed) messages.debug("on_field_state_changed");         
+           if (debug_deep) messages.debug("on_field_state_changed");       
             switch(data.request) {
             case FIELD_REQUEST_ADD:
             case FIELD_REQUEST_ADD_RESET:
-                this.set_checkbox(data.value, true);
+                this.set_checkbox_from_data(data.value, true);
                 break;
             case FIELD_REQUEST_REMOVE:
             case FIELD_REQUEST_REMOVE_RESET:
-                this.set_checkbox(data.value, false);
+                this.set_checkbox_from_data(data.value, false);
                 break;
             default:
                 break;
@@ -271,22 +250,22 @@ googlemap_debug_detailed=false;
         // all : this 
 
         on_all_new_record: function(record) {
-           if (googlemap_debug_detailed) messages.debug("on_all_new_record");
+           if (debug_deep) messages.debug("on_all_new_record");
             this.new_record(record);
         },
 
         on_all_clear_records: function() {
-           if (googlemap_debug) messages.debug("on_all_clear_records");            
+           if (debug) messages.debug("on_all_clear_records");      
         },
 
         on_all_query_in_progress: function() {
-           if (googlemap_debug) messages.debug("on_all_query_in_progress (spinning)");
+           if (debug) messages.debug("on_all_query_in_progress (spinning)");
             // XXX parent
             this.spin();
         },
 
         on_all_query_done: function() {
-           if (googlemap_debug) messages.debug("on_all_query_done");
+           if (debug) messages.debug("on_all_query_done");
 
             // MarkerClusterer
             var markers = [];
@@ -311,12 +290,12 @@ googlemap_debug_detailed=false;
             if (this.received_set) {
                 /* ... and check the ones specified in the resource list */
                 $.each(this.in_set_backlog, function(i, record) {
-                    googlemap.set_checkbox(record, true);
+                    googlemap.set_checkbox_from_record(record, true);
                 });
                // reset 
                googlemap.in_set_backlog = [];
 
-               if (googlemap_debug) messages.debug("unspinning");
+               if (debug) messages.debug("unspinning");
                 this.unspin();
             }
             this.received_all = true;
index 6f9694f..b4c7b29 100644 (file)
@@ -4,6 +4,17 @@
  * License: GPLv3
  */
 
+/* 
+ * WARNINGS
+ *
+ * This is very rough for now and not deemed working
+ * 
+ * Also it still requires adaptation for the init_key / init_id / canonical_key / id business 
+ * if the basic logic was to become usable
+ * 
+ * WARNINGS
+ */
+
 /* ongoing adaptation to slickgrid 
    still missing are
 . checkboxes really running properly
index b7b92f4..b435b38 100644 (file)
@@ -20,13 +20,13 @@ Current implementation makes the following assumptions
   with checkboxes is desired
 * optionally pass columns as the initial set of columns
   if None then this is taken from the query's fields
-* id_key is the name of a column used internally in the plugin
-  for checkboxes management. Caller should specify a column that is present 
-  in the fields returned by 'query' and that has unique values.
+* init_key is the name of a column that should appear in both queries
+  and used internally in the plugin for checkboxes initialization. 
   If not specified, metadata will be used to find out a primary key.
   However in the case of nodes & slice for example, the default key
-  as returned by the metadata would be 'urn', but it is not necessarily 
-  a good idea to show urn's initially - if at all.
+  as returned by the metadata would be 'urn', but 'urn' could only 
+  be used for this purpose if it gets displayed initially, which is
+  not necessarily a good idea.
   This is why a slice view would use 'hrn' here instead.
 * datatables_options are passed to dataTables as-is; 
   however please refrain from passing an 'aoColumns' 
@@ -35,7 +35,7 @@ Current implementation makes the following assumptions
 
     def __init__ (self, query=None, query_all=None, 
                   checkboxes=False, columns=None, 
-                  id_key=None,
+                  init_key=None,
                   datatables_options={}, **settings):
         Plugin.__init__ (self, **settings)
         self.query          = query
@@ -57,7 +57,7 @@ Current implementation makes the following assumptions
         else:
             self.columns = []
             self.hidden_columns = []
-        self.id_key=id_key
+        self.init_key=init_key
         self.datatables_options=datatables_options
         # if checkboxes were required, we tell datatables about this column's type
         # so that sorting can take place on a selected-first basis (or -last of course)
@@ -106,4 +106,4 @@ Current implementation makes the following assumptions
         return ['plugin_uuid', 'domid', 
                 'query_uuid', 'query_all_uuid', 
                 'checkboxes', 'datatables_options', 
-                'hidden_columns', 'id_key',]
+                'hidden_columns', 'init_key',]
index 39e5049..935c087 100644 (file)
@@ -7,7 +7,7 @@
 (function($){
 
     var debug=false;
-//    debug=true
+    debug=true
 
     var QueryTable = Plugin.extend({
 
             var query = manifold.query_store.find_analyzed_query(this.options.query_uuid);
             this.object = query.object;
 
-           // xxx beware that this.key needs to contain a key that all records will have
-           // in general query_all will return well populated records, but query
-           // returns records with only the fields displayed on startup. 
-           this.key = (this.options.id_key);
-           if (typeof(this.key)=='undefined' || (this.key).startsWith("unknown")) {
-                   // if not specified by caller, decide from metadata
-                   var keys = manifold.metadata.get_key(this.object);
-                   this.key = (keys && keys.length == 1) ? keys[0] : null;
-           }
-           if (! this.key) messages.warning("querytable.init could not kind valid key");
-           messages.debug("querytable: key="+this.key);
+           //// we need 2 different keys
+           // * canonical_key is the primary key as derived from metadata (typically: urn)
+           //   and is used to communicate about a given record with the other plugins
+           // * init_key is a key that both kinds of records 
+           //   (i.e. records returned by both queries) must have (typically: hrn or hostname)
+           //   in general query_all will return well populated records, but query
+           //   returns records with only the fields displayed on startup
+           var keys = manifold.metadata.get_key(this.object);
+           this.canonical_key = (keys && keys.length == 1) ? keys[0] : undefined;
+           // 
+           this.init_key = this.options.init_key;
+           // have init_key default to canonical_key
+           this.init_key = this.init_key || this.canonical_key;
+           // sanity check
+           if ( ! this.init_key ) messages.warning ("QueryTable : cannot find init_key");
+           if ( ! this.canonical_key ) messages.warning ("QueryTable : cannot find canonical_key");
+           if (debug) messages.debug("querytable: canonical_key="+this.canonical_key+" init_key="+this.init_key);
 
             /* Setup query and record handlers */
             this.listen_query(options.query_uuid);
             return (tabIndex.length > 0) ? tabIndex[0] : -1;
         }, // getColIndex
 
-        checkbox_html : function (key, value)
-        {
-//         if (debug) messages.debug("checkbox_html, value="+value);
+       // create a checkbox <input> tag
+       // computes 'id' attribute from canonical_key
+       // computes 'init_id' from init_key for initialization phase
+       // no need to used convoluted ids with plugin-uuid or others, since
+       // we search using table.$ which looks only in this table
+        checkbox_html : function (record) {
             var result="";
             // Prefix id with plugin_uuid
             result += "<input";
             result += " class='querytable-checkbox'";
-            result += " id='" + this.flat_id(this.id('checkbox', value)) + "'";
-            result += " name='" + key + "'";
+        // compute id from canonical_key
+           var id = record[this.canonical_key]
+//         if (debug) messages.debug("checkbox_html, id="+id);
+        // compute init_id form init_key
+           var init_id=record[this.init_key];
+        // set id - for retrieving from an id, or for posting events upon user's clicks
+           result += " id='"+record[this.canonical_key]+"'";
+        // set init_id
+           result += "init_id='" + init_id + "'";
+        // wrap up
             result += " type='checkbox'";
             result += " autocomplete='off'";
-           if (value === undefined) {
-               messages.warning("querytable.checkbox_html - undefined value");
-           } else {
-               result += " value='" + value + "'";
-           }
             result += "></input>";
             return result;
         }, 
             // catch up with the last column if checkboxes were requested 
             if (this.options.checkboxes) {
                 // Use a key instead of hostname (hard coded...)
-                line.push(this.checkbox_html(this.key, record[this.key]));
+                line.push(this.checkbox_html(record));
                }
     
            // adding an array in one call is *much* more efficient
                 this.table.fnSetColumnVis(index, false);
         },
 
-        set_checkbox: function(record, checked)
-        {
-            /* Default: checked = true */
+       // this is used at init-time, at which point only init_key can make sense
+       // (because the argument record, if it comes from query, might not have canonical_key set
+       set_checkbox_from_record: function (record, checked) {
             if (checked === undefined) checked = true;
-
-            var id;
-            /* The function accepts both records and their key */
-            switch (manifold.get_type(record)) {
-            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 (id === undefined) {
-                       messages.warning("querytable.set_checkbox record has no id to figure which line to tick");
-                       return;
-               }
-            // PB TO CHECK THE RIGHT CHECKBOXES IS HERE... flat_id using \ in the key
-            // need to use escape_id when creating the id of the checkboxes
-            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);
-            // As we are using [id="x"] syntax, we need to remove the # in the checkbox_id
-            checkbox_id = checkbox_id.replace("#","");
-            // using dataTables's $ to search also in nodes that are not currently displayed
-            var element = this.table.$('[id="' + checkbox_id + '"]');
-            if (debug) 
-                messages.debug("set_checkbox checked=" + checked
-                               + " id=" + checkbox_id + " matches=" + element.length);
-            element.attr('checked', checked);
-        },
+           var init_id = record[this.init_key];
+           if (debug) messages.debug("set_checkbox_from_record, init_id="+init_id);
+           // using table.$ to search inside elements that are not visible
+           var element = this.table.$('[init_id="'+init_id+'"]');
+           element.attr('checked',checked);
+       },
+
+       // id relates to canonical_key
+       set_checkbox_from_data: function (id, checked) {
+            if (checked === undefined) checked = true;
+           if (debug) messages.debug("set_checkbox_from_data, id="+id);
+           // using table.$ to search inside elements that are not visible
+           var element = this.table.$("[id='"+id+"']");
+           element.attr('checked',checked);
+       },
 
         /*************************** QUERY HANDLER ****************************/
 
         {
             if (this.received_all_query) {
                // if the 'all' query has been dealt with already we may turn on the checkbox
-                this.set_checkbox(record, true);
+                this.set_checkbox_from_record(record, true);
             } else {
                // otherwise we need to remember that and do it later on
-               if (debug) messages.debug("Remembering record to check " + record[this.key]);
+               if (debug) messages.debug("Remembering record to check " + record[this.init_key]);
                 this.buffered_records_to_check.push(record);
             }
         },
             switch(data.request) {
                 case FIELD_REQUEST_ADD:
                 case FIELD_REQUEST_ADD_RESET:
-                    this.set_checkbox(data.value, true);
+                    this.set_checkbox_from_data(data.value, true);
                     break;
                 case FIELD_REQUEST_REMOVE:
                 case FIELD_REQUEST_REMOVE_RESET:
-                    this.set_checkbox(data.value, false);
+                    this.set_checkbox_from_data(data.value, false);
                     break;
                 default:
                     break;
             switch(data.request) {
                 case FIELD_REQUEST_ADD:
                 case FIELD_REQUEST_ADD_RESET:
-                    this.set_checkbox(data.value, true);
+                    this.set_checkboxfrom_data(data.value, true);
                     break;
                 case FIELD_REQUEST_REMOVE:
                 case FIELD_REQUEST_REMOVE_RESET:
-                    this.set_checkbox(data.value, false);
+                    this.set_checkbox_from_data(data.value, false);
                     break;
                 default:
                     break;
            // checkboxes on the fly at that time (dom not yet created)
             $.each(this.buffered_records_to_check, function(i, record) {
                if (debug) messages.debug ("delayed turning on checkbox " + i + " record= " + record);
-                self.set_checkbox(record, true);
+                self.set_checkbox_from_record(record, true);
             });
            this.buffered_records_to_check = [];
 
             e.stopPropagation();
 
             var self = e.data;
+           var id=this.id;
 
-            // XXX this.value = key of object to be added... what about multiple keys ?
-           if (debug) messages.debug("querytable click handler checked=" + this.checked + " "+this.key+"=" + this.value);
-            manifold.raise_event(self.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, this.value);
+            // this.id = key of object to be added... what about multiple keys ?
+           if (debug) messages.debug("querytable._check_click key="+this.canonical_key+"->"+id+" checked="+this.checked);
+            manifold.raise_event(self.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, id);
             //return false; // prevent checkbox to be checked, waiting response from manifold plugin api
             
         },
index 7d7de24..e87b54f 100644 (file)
             var row;
            
            // make sure the change is visible : toggle on the whole plugin
-           // this might hae to be made an 'auto-toggle' option of this plugin..
+           // this might have to be made an 'auto-toggle' option of this plugin..
            // also it might be needed to be a little finer-grained here
            this.toggle_on();
            
index f0e7a4a..429b2d8 100644 (file)
@@ -9,8 +9,10 @@ from ui.topmenu                         import topmenu_items, the_user
 from django.http                        import HttpResponse, HttpResponseRedirect
 from django.contrib                     import messages
 from django.contrib.auth.decorators     import login_required
+from django.core.mail                   import send_mail
+
 #
-import json, os, re
+import json, os, re, itertools
 
 # requires login
 class AccountView(LoginRequiredAutoLogoutView):
@@ -32,7 +34,7 @@ class AccountView(LoginRequiredAutoLogoutView):
             if user_detail['config']:
                 config = json.loads(user_detail['config'])
 
-        platform_query  = Query().get('local:platform').select('platform_id','platform')
+        platform_query  = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled')
         account_query  = Query().get('local:account').select('user_id','platform_id','auth_type','config')
         platform_details = execute_query(self.request, platform_query)
         account_details = execute_query(self.request, account_query)
@@ -42,41 +44,124 @@ class AccountView(LoginRequiredAutoLogoutView):
         account_type = ''
         account_usr_hrn = ''
         account_pub_key = ''
+        account_reference = ''
         platform_name_list = []
+        platform_name_secondary_list = []
+        platform_access_list = []
+        platform_no_access_list = []
+        total_platform_list = []
         account_type_list = []
+        account_type_secondary_list = []
+        account_reference_list = []
+        delegation_type_list = []
+        exp_user_cred_list = []
+        slice_list = []
+        auth_list = []
+        slice_cred_exp_list = []
+        auth_cred_exp_list = []
         usr_hrn_list = []
-        pub_key_list = []          
-        for account_detail in account_details:
-            for platform_detail in platform_details:
+        pub_key_list = []
+          
+        for platform_detail in platform_details:
+            if 'sfa' in platform_detail['gateway_type'] and platform_detail['disabled']==0:
+                total_platform = platform_detail['platform']
+                total_platform_list.append(total_platform)
+                
+            for account_detail in account_details:
                 if platform_detail['platform_id'] == account_detail['platform_id']:
                     platform_name = platform_detail['platform']
-                    account_type = account_detail['auth_type']
                     account_config = json.loads(account_detail['config'])
-                    # a bit more pythonic
                     account_usr_hrn = account_config.get('user_hrn','N/A')
                     account_pub_key = account_config.get('user_public_key','N/A')
-                    
-                    platform_name_list.append(platform_name)
-                    account_type_list.append(account_type)
-                    usr_hrn_list.append(account_usr_hrn)
-                    pub_key_list.append(account_pub_key)
-            
-                # to hide private key row if it doesn't exist    
-                if 'myslice' in platform_detail['platform']:
-                    account_config = json.loads(account_detail['config'])
-                    account_priv_key = account_config.get('user_private_key','N/A')
-        
-        # combining 4 lists into 1 [to render in the template] 
-        lst = [{'platform_name': t[0], 'account_type': t[1], 'usr_hrn':t[2], 'usr_pubkey':t[3]} 
-               for t in zip(platform_name_list, account_type_list, usr_hrn_list, pub_key_list)]
+                    account_reference = account_config.get ('reference_platform','N/A')
+                    # credentials
+                    acc_slice_cred = account_config.get('delegated_slice_credentials','N/A')
+                    acc_auth_cred = account_config.get('delegated_authority_credentials','N/A')
+
+                    if 'N/A' not in acc_slice_cred:
+                        for key, value in acc_slice_cred.iteritems():
+                            slice_list.append(key)
+                            # get cred_exp date
+                            exp_date = re.search('<expires>(.*)</expires>', value)
+                            if exp_date:
+                                exp_date = exp_date.group(1)
+                                slice_cred_exp_list.append(exp_date)
+
+                        my_slices = [{'slice_name': t[0], 'cred_exp': t[1]}
+                            for t in zip(slice_list, slice_cred_exp_list)]
+
+                    if 'N/A' not in acc_auth_cred:
+                        for key, value in acc_auth_cred.iteritems():
+                            auth_list.append(key)
+                        #get cred_exp date
+                            exp_date = re.search('<expires>(.*)</expires>', value)
+                            if exp_date:
+                                exp_date = exp_date.group(1)
+                                auth_cred_exp_list.append(exp_date)
+
+                        my_auths = [{'auth_name': t[0], 'cred_exp': t[1]}
+                            for t in zip(auth_list, auth_cred_exp_list)]
+
+
+                    account_user_credential = account_config.get('delegated_user_credential','N/A')
+                    # Expiration date 
+                    result = re.search('<expires>(.*)</expires>', account_user_credential)
+                    if result:
+                        exp_user_cred = result.group(1)
+                    # for reference accounts
+                    if 'reference' in account_detail['auth_type']:
+                        account_type = 'Reference'
+                        delegation = 'N/A'
+                        platform_name_secondary_list.append(platform_name)
+                        account_type_secondary_list.append(account_type)
+                        account_reference_list.append(account_reference)
+                        secondary_list = [{'platform_name': t[0], 'account_type': t[1], 'account_reference': t[2]} 
+                            for t in zip(platform_name_secondary_list, account_type_secondary_list, account_reference_list)]
+                       
+                    elif 'managed' in account_detail['auth_type']:
+                        account_type = 'Principal'
+                        delegation = 'Automatic'
+                    else:
+                        account_type = 'Principal'
+                        delegation = 'Manual'
+                    # for principal (auth_type=user/managed) accounts
+                    if 'reference' not in account_detail['auth_type']:
+                        platform_name_list.append(platform_name)
+                        account_type_list.append(account_type)
+                        delegation_type_list.append(delegation)
+                        exp_user_cred_list.append(exp_user_cred)
+                        usr_hrn_list.append(account_usr_hrn)
+                        pub_key_list.append(account_pub_key)
+                        # combining 5 lists into 1 [to render in the template] 
+                        lst = [{'platform_name': t[0], 'account_type': t[1], 'delegation_type': t[2], 'credential_expiration':t[3], 'usr_hrn':t[4], 'usr_pubkey':t[5]} 
+                            for t in zip(platform_name_list, account_type_list, delegation_type_list, exp_user_cred_list, usr_hrn_list, pub_key_list)]
+                    # to hide private key row if it doesn't exist    
+                    if 'myslice' in platform_detail['platform']:
+                        account_config = json.loads(account_detail['config'])
+                        account_priv_key = account_config.get('user_private_key','N/A')
+                    if 'sfa' in platform_detail['gateway_type']:
+                        platform_access = platform_detail['platform']
+                        platform_access_list.append(platform_access)
+       
+        # Removing the platform which already has access
+        for platform in platform_access_list:
+            total_platform_list.remove(platform)
+        # we could use zip. this one is used if columns have unequal rows 
+        platform_list = [{'platform_no_access': t[0]}
+            for t in itertools.izip_longest(total_platform_list)]
+
 
         context = super(AccountView, self).get_context_data(**kwargs)
         context['data'] = lst
+        context['ref_acc'] = secondary_list
+        context['platform_list'] = platform_list
+        context['my_slices'] = my_slices
+        context['my_auths'] = my_auths
         context['person']   = self.request.user
-        context ['firstname'] = config.get('firstname',"?")
-        context ['lastname'] = config.get('lastname',"?")
-        context ['fullname'] = context['firstname'] +' '+ context['lastname']
-        context ['authority'] = config.get('authority',"Unknown Authority")
+        context['firstname'] = config.get('firstname',"?")
+        context['lastname'] = config.get('lastname',"?")
+        context['fullname'] = context['firstname'] +' '+ context['lastname']
+        context['authority'] = config.get('authority',"Unknown Authority")
         context['user_private_key'] = account_priv_key
         
         # XXX This is repeated in all pages
@@ -273,8 +358,62 @@ def account_process(request):
         else:
             messages.error(request, 'Account error: You need an account in myslice platform to perform this action')    
             return HttpResponseRedirect("/portal/account/")
-           
-       
+        
+    elif 'fuseco' in request.POST:
+        # The recipients are the PI of the authority
+        #recipients = authority_get_pi_emails(request, authority_hrn)
+        recipients = ["support@myslice.info"] 
+        requester = request.user # current user
+        sender = 'yasin.upmc@gmail.com' # the server email
+        msg = "OneLab user %s requested account in fuseco Platform" % requester
+        send_mail("Onelab user %s requested an account in Fuseco"%requester , msg, sender, recipients)
+        messages.info(request, 'Request to get access on Fuseco platform received. Please wait for PI\'s reply.')
+        return HttpResponseRedirect("/portal/account/")
+
+    elif 'ple' in request.POST:
+        # The recipients are the PI of the authority
+        #recipients = authority_get_pi_emails(request, authority_hrn)
+        recipients = ["support@myslice.info"] 
+        requester = request.user # current user
+        sender = 'yasin.upmc@gmail.com' # the server email
+        msg = "OneLab user %s requested account in fuseco Platform" % requester
+        send_mail("Onelab user %s requested an account in PLE"%requester , msg, sender, recipients)
+        messages.info(request, 'Request to get access on PLE platform received. Please wait for PI\'s reply.')
+        return HttpResponseRedirect("/portal/account/")
+
+    elif 'omf' in request.POST:
+        # The recipients are the PI of the authority
+        #recipients = authority_get_pi_emails(request, authority_hrn)
+        recipients = ["support@myslice.info"]
+        requester = request.user # current user
+        sender = 'yasin.upmc@gmail.com' # the server email
+        msg = "OneLab user %s requested account in omf:nitos Platform" % requester
+        send_mail("Onelab user %s requested an account in OMF:NITOS"%requester , msg, sender, recipients)
+        messages.info(request, 'Request to get access on OMF:NITOS platform received. Please wait for PI\'s reply.')
+        return HttpResponseRedirect("/portal/account/")
+
+    elif 'wilab' in request.POST:
+        # The recipients are the PI of the authority
+        #recipients = authority_get_pi_emails(request, authority_hrn)
+        recipients = ["support@myslice.info"]
+        requester = request.user # current user
+        sender = 'yasin.upmc@gmail.com' # the server email
+        msg = "OneLab user %s requested account in Wilab Platform" % requester
+        send_mail("Onelab user %s requested an account in Wilab"%requester , msg, sender, recipients)
+        messages.info(request, 'Request to get access on Wilab platform received. Please wait for PI\'s reply.')
+        return HttpResponseRedirect("/portal/account/")
+
+    elif 'iotlab' in request.POST:
+        # The recipients are the PI of the authority
+        #recipients = authority_get_pi_emails(request, authority_hrn)
+        recipients = ["support@myslice.info"]
+        requester = request.user # current user
+        sender = 'yasin.upmc@gmail.com' # the server email
+        msg = "OneLab user %s requested account in IOTLab Platform" % requester
+        send_mail("Onelab user %s requested an account in IOTLab"%requester , msg, sender, recipients)
+        messages.info(request, 'Request to get access on IOTLab platform received. Please wait for PI\'s reply.')
+        return HttpResponseRedirect("/portal/account/")
+  
     else:
         messages.info(request, 'Under Construction. Please try again later!')
         return HttpResponseRedirect("/portal/account/")
index 032b648..618e0bb 100644 (file)
@@ -71,7 +71,8 @@ class SliceView (LoginRequiredAutoLogoutView):
         main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
         main_query.select(
                 'slice_hrn',
-                'resource.hrn', 'resource.urn', 'resource.hostname', 'resource.type', 
+                #'resource.hrn', 'resource.urn', 
+                'resource.hostname', 'resource.type', 
                 'resource.network_hrn',
                 'lease.urn',
                 'user.user_hrn',
@@ -79,7 +80,7 @@ class SliceView (LoginRequiredAutoLogoutView):
         )
         # for internal use in the querytable plugin;
         # needs to be a unique column present for each returned record
-        #main_query_key = 'hrn'
+        main_query_init_key = 'hostname'
     
         query_resource_all = Query.get('resource').select(resource_fields)
 
@@ -198,7 +199,10 @@ class SliceView (LoginRequiredAutoLogoutView):
             togglable  = False,
             query      = sq_resource,
             query_all  = query_resource_all,
+            # this key is the one issued by google
             googlemap_api_key = Config().googlemap_api_key(),
+            # the key to use at init-time
+            init_key   = main_query_init_key,
             checkboxes = True,
             # center on Paris
             latitude   = 49.,
@@ -221,8 +225,8 @@ class SliceView (LoginRequiredAutoLogoutView):
             # this is the query at the core of the slice list
             query      = sq_resource,
             query_all  = query_resource_all,
-            # safer to use 'hrn' as the internal unique key for this plugin
-            #id_key     = main_query_key,
+            # use 'hrn' as the internal unique key for this plugin
+            init_key     = main_query_init_key,
             checkboxes = True,
             datatables_options = { 
                 'iDisplayLength': 25,
@@ -239,8 +243,9 @@ class SliceView (LoginRequiredAutoLogoutView):
                 # this is the query at the core of the slice list
                 query      = sq_resource,
                 query_all  = query_resource_all,
-                # safer to use 'hrn' as the internal unique key for this plugin
-                id_key     = main_query_key,
+                # use 'hrn' as the internal unique key for this plugin
+                # xxx todo on querygrid as well
+                # init_key     = main_query_init_key,
                 checkboxes = True,
                 )
 
@@ -270,8 +275,6 @@ class SliceView (LoginRequiredAutoLogoutView):
         if insert_grid:
             resources_sons.append(resources_as_grid)
 
-        print 40*'+-',"resources_sons has",len(resources_sons),"son"
-
         resources_area = Tabs ( page=page, 
                                 domid="resources",
                                 togglable=True,
index 3173e9c..f445aee 100644 (file)
@@ -216,8 +216,8 @@ h1 {
 }
 
 #info {
-  background-color : #ded;
-  border : 1px solid #0a0;
+  background-color : #839E99;
+  border : 1px solid #839E99;
   padding : 1em;
   margin-bottom : 1em;
 }
index 1133452..78e6b7f 100644 (file)
@@ -10,7 +10,7 @@
 
 {% block unfold_main %}
 
-<h2>MySlice  Account</h2>
+<h2>User Details</h2>
 
 {% if messages %}
 <ul class="messages">
 {% endif %}
 
 
+<form id="editForm"  method="POST" action="account_process" enctype="multipart/form-data">
 <div id="middle" align="center">
        <div class="well">
-               <form id="editForm"  method="POST" action="account_process" enctype="multipart/form-data">
                {% csrf_token %}
                        <table class="profile">          
                        <tr class="odd">
                                <td colspan="2">
-                                               <div id="info">Personal Details</div>
+                                               <div id="info">Platform: Myslice</div>
                                </td>
                        </tr>
                        <tr class="even">
                                </td>
                                </tr>
                        </table>
-               </form>
        </div>
 </div>
 
-<h2>Platform Access</h2>
+<h2>Account Information</h2>
+<h3>Principal Account</h3>
 <div id="middle" align="center">
        <div class="well">
                <table class="mytable"> 
                        <tr class="odd"> 
                        <th>Platform</th> 
-                       <th>Account Type</th> 
+                       <th>Account Type</th>
+                               <th>Account_delegation</th>
                        <th>user_hrn</th>
-                       <th>Pub Key</th>
+                       <!--<th>Pub Key</th> -->
                </tr>   
                        {% for row in data %}         
                        <tr class="border_bottom">
                        <td class="odd"> {{ row.platform_name }} </td>
                        <td class="even"> {{ row.account_type }} </td>
+                               <td class="odd"> {{ row.delegation_type }} </td>
                                <td class="odd"> {{ row.usr_hrn }}  </td>
-                       <td class="even"> {{ row.usr_pubkey }} </td>
+               <!--    <td class="even"> {{ row.usr_pubkey }} </td> -->
                </tr> 
                        {%endfor%}               
                </table>
 </div>
 
 
+<h3>Credentials</h3>
+<div id="middle" align="center">
+    <div class="well">
+        <table class="mytable">
+                       <caption><b>Delegated User Credential</b></caption> 
+            <tr class="odd"> 
+                <th>Expiration Date</th>
+            </tr>
+                       {% for row in data %}         
+                       <tr class="border_bottom">
+                       <td class="even"> {{ row.credential_expiration }} </td>
+                       </tr>
+                       {%endfor%}
+                </table>
+               <p><button id="dl_user_cred" name= "dl_user_cred" type="submit" title="Download User Credential">Download</button></p>
+                <table class="mytable">
+                       <caption><b>Delegated Slice Credentials</b></caption>  
+               <tr class="odd"> 
+                               <th>Slice Name</th> 
+                       <th>Expiration Date</th>
+               </tr>
+                       {% for row in my_slices %}     
+               <tr class="border_bottom">
+                       <td class="even"> {{ row.slice_name }} </td>
+                               <td class="odd"> {{ row.cred_exp }} </td>
+               </tr>
+               {%endfor%}
+               </table>
+               <p><button id="dl_slice_cred" name= "dl_slice_cred" type="submit" title="Download Slice Credentials">Download</button></p>
+               <table class="mytable">
+                       <caption><b>Delegated Authority Credentials</b></caption>
+                       <tr class="odd"> 
+                               <th>Authority Name</th> 
+                               <th>Expiration Date</th>
+                       </tr>
+                       {% for row in my_auths %}
+                       <tr class="border_bottom">
+                               <td class="even"> {{ row.auth_name }} </td>
+                               <td class="odd"> {{ row.cred_exp }} </td>
+                       </tr>
+                       {%endfor%}
+               </table>
+               <p><button id="dl_auth_cred" name= "dl_auth_cred" type="submit" title="Download Authority Credentials">Download</button></p>
+               <p></p> 
+               <p><button id="clear_cred" name= "clear_cred" type="submit" title="Clear All Credentials">Clear Credentials</button></p>
+    </div>
+</div>
+
+
+
+
+<h3>Testbed Accounts</h3>
+<div id="middle" align="center">
+    <div class="well">
+        <table class="mytable"> 
+            <tr class="odd"> 
+                <th>Platform</th> 
+                <th>Account Type</th>
+                               <th>Reference to</th>
+            </tr>   
+            {% for row in ref_acc %}         
+            <tr class="border_bottom">
+                <td class="odd"> {{ row.platform_name }} </td>
+                <td class="even"> {{ row.account_type }} </td>
+                               <td class="odd"> {{ row.account_reference }} </td>
+            </tr> 
+            {%endfor%}               
+        </table>
+    </div>
+</div>
+
+<h3>Add Accounts</h3>
+<div id="middle" align="center">
+    <div class="well">
+        <table class="mytable"> 
+            <tr class="odd"> 
+                <th>Platforms</th> 
+                <th>Request Access</th>
+            </tr>   
+            {% for platform in platform_list %}         
+            <tr class="border_bottom">
+                <td class="odd"> {{ platform.platform_no_access }} </td>
+                <td class="even">
+                                       <button id="request_access" name= {{platform.platform_no_access}} type="submit" title="Add account to this platform">Add {{platform.platform_no_access}}</button>
+                               </td>
+            </tr> 
+            {%endfor%}               
+        </table>
+    </div>
+</div>
+
+</form>
 
 {% endblock %}
index f0f2391..014e143 100644 (file)
@@ -24,7 +24,8 @@ class SimpleGridView (TemplateView):
         main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
         main_query.select(
                 'slice_hrn',
-                'resource.hrn', 'resource.hostname', 'resource.type', 
+                #'resource.hrn', 
+                'resource.hostname', 'resource.type', 
                 'resource.network_hrn',
                 'lease.urn',
                 'user.user_hrn',
@@ -32,7 +33,7 @@ class SimpleGridView (TemplateView):
         )
         # for internal use in the querygrid plugin;
         # needs to be a unique column present for each returned record
-        main_query_key = 'hrn'
+        main_query_init_key = 'hostname'
     
         query_resource_all = Query.get('resource').select(resource_fields)
         
@@ -50,7 +51,7 @@ class SimpleGridView (TemplateView):
             query      = sq_resource,
             query_all  = query_resource_all,
             # safer to use 'hrn' as the internal unique key for this plugin
-            id_key     = main_query_key,
+            id_key     = main_query_init_key,
             checkboxes = True,
             datatables_options = { 
                 'iDisplayLength': 25,
diff --git a/trash/simpletableview.py b/trash/simpletableview.py
new file mode 100644 (file)
index 0000000..e0fd770
--- /dev/null
@@ -0,0 +1,84 @@
+# just one instance of QueryTable, nothing more, nothing less
+from django.views.generic.base import TemplateView
+from django.template import RequestContext
+from django.shortcuts import render_to_response
+
+from manifold.core.query import Query, AnalyzedQuery
+
+from unfold.page import Page
+
+from ui.topmenu import topmenu_items, the_user
+
+from plugins.querytable import QueryTable
+
+class SimpleTableView (TemplateView):
+
+    def get (self, request, slicename='ple.inria.f14'):
+
+        page=Page(request)
+        page.expose_js_metadata()
+        metadata = page.get_metadata()
+        resource_md = metadata.details_by_object('resource')
+        resource_fields = [column['name'] for column in resource_md['column']]
+
+        main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
+        main_query.select(
+                'slice_hrn',
+                'resource.hostname', 'resource.type', 
+                'resource.network_hrn',
+                'lease.urn',
+                'user.user_hrn',
+                #'application.measurement_point.counter'
+        )
+        # for internal use in the querytable plugin;
+        # needs to be a unique column present for each returned record
+        main_query_init_key = 'hostname'
+    
+        query_resource_all = Query.get('resource').select(resource_fields)
+        
+        aq = AnalyzedQuery(main_query, metadata=metadata)
+        page.enqueue_query(main_query, analyzed_query=aq)
+        page.enqueue_query(query_resource_all)
+
+        sq_resource    = aq.subquery('resource')
+
+        resources_as_list = QueryTable( 
+            page       = page,
+            domid      = 'resources-list',
+            title      = 'List view',
+            # this is the query at the core of the slice list
+            query      = sq_resource,
+            query_all  = query_resource_all,
+            # safer to use 'hrn' as the internal unique key for this plugin
+            init_key     = main_query_init_key,
+            checkboxes = True,
+            datatables_options = { 
+                'iDisplayLength': 25,
+                'bLengthChange' : True,
+                'bAutoWidth'    : True,
+                },
+            )
+
+        # variables that will get passed to the view-unfold1.html template
+        template_env = {}
+        
+        # define 'unfold_main' to the template engine - the main contents
+        template_env [ 'unfold_main' ] = resources_as_list.render(request)
+    
+        # more general variables expected in the template
+        template_env [ 'title' ] = 'simple %(slicename)s'%locals()
+        # the menu items on the top
+        template_env [ 'topmenu_items' ] = topmenu_items('Slice', request) 
+        # so we can sho who is logged
+        template_env [ 'username' ] = the_user (request) 
+    
+        # don't forget to run the requests
+        page.expose_queries ()
+
+        # the prelude object in page contains a summary of the requirements() for all plugins
+        # define {js,css}_{files,chunks}
+        prelude_env = page.prelude_env()
+        template_env.update(prelude_env)
+        result=render_to_response ('view-unfold1.html',template_env,
+                                   context_instance=RequestContext(request))
+        return result
index a1041cc..5e14d97 100644 (file)
@@ -1,5 +1,6 @@
 from django.conf.urls           import patterns, include, url
 
+import trash.simpletableview
 import trash.simplegridview
 
 urlpatterns = patterns(
@@ -8,5 +9,6 @@ urlpatterns = patterns(
     url(r'^scroll/?$',          'trash.sampleviews.scroll_view'),
     url(r'^plugin/?$',          'trash.pluginview.test_plugin_view'),
     url(r'^dashboard/?$',       'trash.dashboard.dashboard_view'),
+    url(r'^simpletable/(?P<slicename>[\w\.]+)/?$', trash.simpletableview.SimpleTableView.as_view()),
     url(r'^simplegrid/(?P<slicename>[\w\.]+)/?$', trash.simplegridview.SimpleGridView.as_view()),
 )