ColumnsEditor: based on query_editor plugin, simplified, without autocomplete filters
authorLoic Baron <loic.baron@lip6.fr>
Fri, 28 Mar 2014 18:37:27 +0000 (19:37 +0100)
committerLoic Baron <loic.baron@lip6.fr>
Fri, 28 Mar 2014 18:37:27 +0000 (19:37 +0100)
12 files changed:
plugins/columns_editor/__init__.py [new file with mode: 0644]
plugins/columns_editor/static/css/query_editor.css [new file with mode: 0644]
plugins/columns_editor/static/img/details_close.png [new file with mode: 0644]
plugins/columns_editor/static/img/myslice-icon-filter.png [new file with mode: 0644]
plugins/columns_editor/static/js/columns_editor.js [new file with mode: 0644]
plugins/columns_editor/templates/columns_editor.html [new file with mode: 0644]
plugins/columns_editor/templates/filter_input_integer.html [new file with mode: 0644]
plugins/columns_editor/templates/filter_input_others.html [new file with mode: 0644]
plugins/columns_editor/templates/filter_input_string.html [new file with mode: 0644]
plugins/columns_editor/templates/filter_input_string_values.html [new file with mode: 0644]
portal/sliceresourceview.py
portal/templates/slice-resource-view.html

diff --git a/plugins/columns_editor/__init__.py b/plugins/columns_editor/__init__.py
new file mode 100644 (file)
index 0000000..3cc48a9
--- /dev/null
@@ -0,0 +1,91 @@
+from unfold.plugin import Plugin
+
+from django.template.loader import render_to_string
+
+# XXX We need naming helpers in the python Plugin class also, used in template
+
+class ColumnsEditor(Plugin):
+    def __init__ (self, query, query_all = None, **settings):
+        Plugin.__init__ (self, **settings)
+        self.query=query
+        self.query_uuid = query.query_uuid
+        self.query_all = query_all
+        self.query_all_uuid = query_all.query_uuid if query_all else None
+
+    def template_file(self):
+        return "columns_editor.html"
+
+    def requirements (self):
+        reqs = {
+            'js_files' : [
+                # XXX datatables
+                'js/columns_editor.js',
+            ] ,
+            'css_files': [
+                'css/query_editor.css',
+                'css/jquery-ui.css',
+            ]
+        }
+        return reqs
+
+    def export_json_settings (self):
+        return True
+
+    def template_env(self, request):
+        fields = []
+        #hidden_columns = self.hidden_columns
+        metadata = self.page.get_metadata()
+        md_fields = metadata.details_by_object('resource')
+
+        # XXX use django templating system here
+        for md_field in md_fields['column']:
+            if md_field['type'] == 'string':
+                if 'allowed_values' in md_field:
+                    allowed_values = md_field['allowed_values'].split(',')
+
+                    options = []
+                    for v in allowed_values:
+                        v_desc = v.split('-')
+                        options.append(v_desc[0])
+
+                    env = {
+                        'domid': self.domid,
+                        'options': options
+                    }
+                    filter_input = render_to_string('filter_input_string_values.html', env)
+                else:
+                    env = {
+                        'domid': self.domid,
+                        'field': md_field['name']
+                    }
+                    filter_input = render_to_string('filter_input_string.html', env)
+                    
+            elif md_field['type'] == 'int':
+                allowed_values = md_field.get('allowed_values', '0,0').split(',')
+                env = {
+                    'domid': self.domid,
+                    'field': md_field['name'],
+                    'min'  : allowed_values[0],
+                    'max'  : allowed_values[1]
+                }
+                filter_input = render_to_string('filter_input_integer.html', env)
+            else:
+                env = {
+                    'domid': self.domid,
+                    'field': md_field['name']
+                }
+                filter_input = render_to_string('filter_input_others.html', env)
+
+            fields.append({
+                'name':          md_field['name'],
+                'type':          md_field['type'],
+                'resource_type': 'N/A',
+                'filter_input':  filter_input,
+                'header':        None,
+                'checked':       md_field['name'] in self.query.get_select()
+            })
+        #return { 'fields': fields, 'hidden_columns': hidden_columns }
+        #return { 'fields': fields , 'query_uuid': self.query_uuid, 'query_all_uuid': self.query_all_uuid }
+        return { 'fields': fields }
+
+    def json_settings_list (self): return ['plugin_uuid', 'domid', 'query_uuid', 'query_all_uuid', ]
diff --git a/plugins/columns_editor/static/css/query_editor.css b/plugins/columns_editor/static/css/query_editor.css
new file mode 100644 (file)
index 0000000..4061d0f
--- /dev/null
@@ -0,0 +1,360 @@
+div.query-editor-spacer {
+    padding: 15px 5px;
+}
+
+table.query-editor {
+    width: 95%;
+    padding-bottom: 10px;
+}
+
+/* Add a scrollbar to autocomplete fields */
+.ui-autocomplete {
+    max-height: 100px;
+    overflow-y: auto;
+    /* prevent horizontal scrollbar */
+    overflow-x: hidden;
+    /* add padding to account for vertical scrollbar */
+    padding-right: 20px;
+    
+    /* NEED TO BE IMPROVED LATER... */
+    /* How to use properties from content class in /templates/myslice/css/myslice.css ? */
+    /* How to factorize this ? Maybe applied differently in other plugins ? */        
+    font-size: 11px;
+}
+.queryeditor-auto-filter{
+    width:200px;
+}
+/* IE 6 doesn't support max-height
+ * we use height instead, but this forces the menu to always be this tall
+ */
+* html .ui-autocomplete {
+    height: 100px;
+}
+
+table.query-editor {
+    margin: 0 auto;
+    clear: both;
+    /* width: 80%;*/
+    width: 300px;
+}
+
+table.query-editor input { 
+    font: normal 12px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+}    
+
+table.query-editor thead th {
+    padding: 3px 18px 3px 3px; 
+    border-bottom: 1px solid black;
+    font-weight: bold;
+    cursor: pointer;
+    * cursor: hand;
+}
+
+table.query-editor tfoot th {
+    padding: 3px 18px 3px 10px;
+    border-top: 1px solid black;
+    font-weight: bold;
+}
+
+table.query-editor td {
+    padding: 2px 5px;
+    font: normal 12px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
+}
+
+table.query-editor td.center, table.query-editor th.center {
+    text-align: center;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.query-editor tr.odd.gradeA {
+    background-color: #ddffdd;
+}
+
+table.query-editor tr.even.gradeA {
+    background-color: #eeffee;
+}
+
+table.query-editor tr.odd.gradeC {
+    background-color: #ddddff;
+}
+
+table.query-editor tr.even.gradeC {
+    background-color: #eeeeff;
+}
+
+table.query-editor tr.odd.gradeX {
+    background-color: #ffdddd;
+}
+
+table.query-editor tr.even.gradeX {
+    background-color: #ffeeee;
+}
+
+table.query-editor tr.odd.gradeU {
+    background-color: #ddd;
+}
+
+table.query-editor tr.even.gradeU {
+    background-color: #eee;
+}
+
+/* change color: T / even -> odd +1 +5 -3*/
+table.query-editor tr.odd.row_sliver td {
+    background-color: #9FAFD1;
+}
+
+table.query-editor tr.even.row_added td {
+    background-color: #b1d19f;
+}
+table.query-editor tr.odd.row_added td {
+    background-color: #a3c98f;
+}
+
+table.query-editor tr.even.row_removed td {
+    background-color: #d9b0b0;
+}
+
+table.query-editor tr.odd.row_removed td {
+    background-color: #d1a09f;
+}
+
+table.query-editor tr.gradeA {
+    background-color: #eeffee;
+}
+
+table.query-editor tr.gradeC {
+    background-color: #ddddff;
+}
+
+table.query-editor tr.gradeX {
+    background-color: #ffdddd;
+}
+
+table.query-editor tr.gradeU {
+    background-color: #ddd;
+}
+
+
+
+
+div.selected{background-color:gray; color:black}
+
+/* icons */
+.myslice-icon-timestamp {
+    background-image: url('images/myslice-icon-timestamp.png') !important;
+}
+.myslice-icon-filter {
+    background-image: url('images/myslice-icon-filter.png') !important;
+}
+.myslice-icon-fields {
+    background-image: url('images/myslice-icon-fields.png') !important;
+}
+.myslice-icon-groups {
+    background-image: url('images/myslice-icon-groups.png') !important;
+}
+.myslice-icon-summary {
+    background-image: url('images/myslice-icon-summary.png') !important;
+}
+.myslice-icon-resources {
+    background-image: url('images/myslice-icon-resources.png') !important;
+}
+.myslice-icon-users {
+    background-image: url('images/myslice-icon-users.png') !important;
+}
+
+a.source-url{
+    font-weight: bold;
+}
+
+span.bold {
+    font-weight: bold;
+}
+
+div#selectdescr {
+    padding-top:2em;
+    color: #555555;
+}
+
+span.short {
+    height:10px;
+}
+
+span.column-title {
+    font-size: 15px;
+    font-weight: bold;
+}
+
+span.column-detail {
+    font-size: 11px; 
+    font-style: italic;
+}
+
+span.group_info {
+    font-size: 11px; 
+    color: green;
+    font-weight: bold;
+}
+
+span.filter_info {
+    color: red;
+    font-weight: bold;
+}
+
+
+/* column configuation style */
+
+OPTION.out{
+    background-color:white;
+    color:black;
+}
+OPTION.in{
+    background-color:#CAE8EA; 
+    color:#4f6b72;
+}
+
+/* jordan disabled
+   div.out{background-color:white; color:black}
+   div.in{background-color:#CAE8EA; color:#4f6b72}
+   div.selected{background-color:gray; color:black}
+   div.invisible{display:none}
+   */
+
+div.note-div {
+    padding: 4px;
+    background-color: #cae8ea;
+    width: 800px;
+    margin-left:auto;
+    margin-right:auto;
+}
+
+div#scrolldiv_old {
+    border : solid 2px grey; 
+    padding:4px; 
+    width:300px; 
+    height:180px; 
+    overflow:auto;
+}
+
+th,td.top {
+    vertical-align: top;
+    text-align: left;
+    padding:10px;
+}
+
+tr.hidden {
+    display:none;
+}
+
+td.smallright {
+    text-align: right;
+    width:20px;
+}
+
+table.center {
+    margin-left:auto; 
+    margin-right:auto;
+}
+
+table.columnlist {
+    width:270px;
+}
+
+table.query-editor td.header {
+    background-color: #CAE8EA;
+    text-align: center;
+    width:30px;
+}
+
+span.header {
+    font-weight: bold;
+    color: #3399CC;
+}
+
+a.source-url{
+    font-weight: bold;
+}
+
+span.menubig {
+    font-size: 16px;
+    font-weight: bold;
+}
+
+span.menusmall {
+    font-size: 14px;
+    font-weight: bold;
+}
+
+span.menuright {
+    font-weight: bold;
+    float: right;
+}
+
+span.simpleright {
+    float: right;
+}
+
+span.gray{
+    color: #555555;
+}
+
+span.short {
+    height:10px;
+}
+
+span.column-title {
+    font-size: 13px;
+    font-weight: bold;
+}
+
+span.column-detail {
+    font-size: 11px; 
+    font-style: italic;
+}
+
+span.myslice_small {
+    font-size: 11px; 
+}
+
+span#username {
+    font-weight: bold;
+    font-size: 1.3em;
+}
+
+.filter_popup{
+    position:relative; /*this is the key*/
+    float: right;
+    z-index:24;
+    background: url('images/myslice-icon-filter.png') no-repeat;
+    /*background-color:#ccc;*/
+    width: 200px;
+    height: 300px;
+    color:#000;
+    text-decoration:none;
+    clear: both;
+}
+
+.filter_popup:hover{
+    z-index:25;
+/*background-color:#ff0*/
+}
+
+.filter_popup span{
+    display: none;
+}
+
+.filter_popup:hover span{ /*the span will display just on :hover state*/
+    display:block;
+    position:absolute;
+    /*top:1em;*/
+    left:-19em;
+    width: 20em;
+    font-size: 8pt;
+    border:1px solid #ccdddd;
+    background-color:#ddeeee;
+    color:#000;
+    text-align: left;
+    padding: 0em 0em 0em 1em;
+}
diff --git a/plugins/columns_editor/static/img/details_close.png b/plugins/columns_editor/static/img/details_close.png
new file mode 100644 (file)
index 0000000..fcc23c6
Binary files /dev/null and b/plugins/columns_editor/static/img/details_close.png differ
diff --git a/plugins/columns_editor/static/img/myslice-icon-filter.png b/plugins/columns_editor/static/img/myslice-icon-filter.png
new file mode 100644 (file)
index 0000000..b2f4ec7
Binary files /dev/null and b/plugins/columns_editor/static/img/myslice-icon-filter.png differ
diff --git a/plugins/columns_editor/static/js/columns_editor.js b/plugins/columns_editor/static/js/columns_editor.js
new file mode 100644 (file)
index 0000000..4ab9b7d
--- /dev/null
@@ -0,0 +1,352 @@
+/**
+ * Description: ColumnsEditor plugin
+ * Copyright (c) 2012-2013 UPMC Sorbonne Universite
+ * License: GPLv3
+ */
+
+// XXX TODO This plugin will be interested in changes in metadata
+// What if we remove a filter, is it removed in the right min/max field ???
+//  -> no on_filter_removed is not yet implemented
+// XXX if a plugin has not declared a handler, it might become inconsistent,
+// and the interface should either reset or disable it
+(function($){
+
+    var ColumnsEditor = Plugin.extend({
+
+        event_filter_added: function(op, suffix) {
+            suffix = (typeof suffix === 'undefined') ? '' : manifold.separator + suffix;
+            var self = this;
+            return function(e, ui) {
+                var array = self.array_from_id(e.target.id);
+                var key   = self.field_from_id(array); // No need to remove suffix...
+
+                // using autocomplete ui
+                if(typeof(ui) != "undefined"){
+                    var value = ui.item.value;
+                }else{
+                    var value = e.target.value;
+                }
+
+                if (value) {
+                    // XXX This should be handled by manifold
+                    //manifold.raise_event(object.options.query_uuid, FILTER_UPDATED, [key, op, value]);
+                    manifold.raise_event(self.options.query_uuid, FILTER_ADDED, [key, op, value]);
+                } else {
+                    // XXX This should be handled by manifold
+                    manifold.raise_event(self.options.query_uuid, FILTER_REMOVED, [key, op]);
+                }
+            };
+        },
+
+        init: function(options, element) {
+            this._super(options, element);
+            this.listen_query(options.query_uuid);
+            // this one is the complete list of resources
+            // and will be bound to callbacks like on_all_new_record
+            this.listen_query(options.query_all_uuid, 'all');
+
+
+            this.elts('queryeditor-auto-filter').change(this.event_filter_added('='));
+            this.elts('queryeditor-filter').change(this.event_filter_added('='));
+            this.elts('queryeditor-filter-min').change(this.event_filter_added('>'));
+            this.elts('queryeditor-filter-max').change(this.event_filter_added('<'));
+
+            var self = this;
+            this.elts('queryeditor-check').click(function() { 
+                manifold.raise_event(self.options.query_uuid, this.checked?FIELD_ADDED:FIELD_REMOVED, this.value);
+            });
+
+            /* The following code adds an expandable column for the table
+            // XXX Why isn't it done statically ?
+            var nCloneTh = document.createElement( 'th' );
+            var nCloneTd = document.createElement( 'td' );
+            nCloneTd.innerHTML = "<span class='glyphicon glyphicon-chevron-right' style='cursor:pointer'></span>";
+            //nCloneTd.innerHTML = '<img src="/components/com_tophat/images/details_open.png">';
+            nCloneTh.innerHTML = '<b>Info</b>';
+            nCloneTd.className = "center";
+            nCloneTh.className = "center";
+            this.elmt('table thead tr').each(function() {
+                this.insertBefore(nCloneTh, this.childNodes[0]);
+            });
+            this.elmt('table tbody tr').each(function() {
+                this.insertBefore(nCloneTd.cloneNode( true ), this.childNodes[0]);
+            });
+            */
+         
+            // We are currently using a DataTable display, but another browsing component could be better
+            //jQuery('#'+this.options.plugin_uuid+'-table').dataTable...
+            /*
+            var  metaTable = this.elmt('table').dataTable({
+// Thierry : I'm turning off all the dataTables options for now, so that
+// the table displays more properly again, might need more tuning though
+//                bFilter     : false,
+//                bPaginate   : false,
+//                bInfo       : false,
+//                sScrollX    : '100%',         // Horizontal scrolling
+//                sScrollY    : '200px',
+//                //bJQueryUI   : true,           // Use jQuery UI
+//                bProcessing : true,           // Loading
+//                aaSorting   : [[ 1, "asc" ]], // sort by column fields on load
+//                aoColumnDefs: [
+//                    { 'bSortable': false, 'aTargets': [ 0 ]},
+//                    { 'sWidth': '8px', 'aTargets': [ 0 ] },
+//                    { 'sWidth': '8px', 'aTargets': [ 4 ] } // XXX NB OF COLS
+//                ]
+            });
+            this.table = metaTable;
+            */
+            this.table = this.elmt('table');
+
+            // Actions on the newly added fields
+            this.elmt('table tbody td span').on('click', function() {
+                var nTr = this.parentNode.parentNode;
+                // use jQuery UI instead of images to keep a common UI
+                // class="glyphicon glyphicon-chevron-down treeclick tree-minus"
+                // East oriented Triangle class="glyphicon-chevron-right"
+                // South oriented Triangle class="glyphicon-chevron-down"
+                
+                if (this.hasClass("glyphicon-chevron-right")) {
+                    this.removeClass("glyphicon-chevron-right").addClass("glyphicon-chevron-down");
+                    // XXX ??????
+                    metaTable.fnOpen(nTr, this.fnFormatDetails(metaTable, nTr, self.options.plugin_uuid+'_div'), 'details' );
+                } else {
+                    this.removeClass("glyphicon-chevron-down").addClass("glyphicon-chevron-right");
+                    metaTable.fnClose(nTr);
+                }
+            });
+
+            this.elmt('table_wrapper').css({
+                'padding-top'   : '0em',
+                'padding-bottom': '0em'
+            });
+            
+            // autocomplete list of tags
+            this.availableTags = {};
+
+        }, // init
+
+        /* UI management */
+
+        check_field: function(field)
+        {
+            this.elmt('check', field).attr('checked', true);
+        },
+
+        uncheck_field: function(field)
+        {
+            this.elmt('check', field).attr('checked', false);
+        },
+
+        update_filter_value: function(filter, removed)
+        {
+            removed = !(typeof removed === 'undefined'); // default = False
+
+            var key   = filter[0];
+            var op    = filter[1];
+            var value = filter[2];
+
+            var id = this.id_from_field(key);
+
+            if (op == '=') {
+                var element = this.elmt(id);
+            } else {
+                var suffix;
+                if (op == '<') {
+                    this.elmt(id, 'max').val(value);
+                } else if (op == '>') {
+                    this.elmt(id, 'min').val(value);
+                } else {
+                    return;
+                }
+                var element = this.elmt(id, suffix);
+            }
+
+            element.val(removed?null:value);
+
+        },
+
+        /* Events */
+
+        on_filter_added: function(filter)
+        {
+            this.update_filter_value(filter);
+        },
+
+        on_filter_removed: function(filter)
+        {
+            this.update_filter_value(filter, true);
+        },
+
+        on_field_added: function(field)
+        {
+            this.check_field(field);
+        },
+
+        on_field_removed: function(field)
+        {
+            this.uncheck_field(field);
+        },
+
+        /* RECORD HANDLERS */
+        on_query_done: function()
+        {
+            //console.log("Query_Editor: query_done!");
+            //console.log(this.availableTags);
+        },
+        /* Autocomplete based on query_all to get all the fields, where query get only the fields selected  */
+        on_all_new_record: function(record)
+        {
+            /*
+            availableTags = this.availableTags;           
+            jQuery.each(record,function(key,value){
+                value = unfold.get_value(value);
+                if(!availableTags.hasOwnProperty(key)){availableTags[key]=new Array();}
+                //availableTags[key].push(value);
+                var currentArray = availableTags[key];
+                if(value!=null){
+                    if(jQuery.inArray(value,currentArray)==-1){availableTags[key].push(value);}
+                }
+           });
+           this.availableTags = availableTags;
+           this.update_autocomplete(availableTags);
+           */
+        },
+
+        /* Former code not used at the moment */
+
+        print_field_description: function(field_header, div_id) 
+        { 
+            //var selected = all_headers[field_header];
+            var selected = getMetadata_field('resource',field_header);
+
+            field_header = div_id+"_"+field_header;
+
+            var output = "<div id='desc"+field_header+"'>";
+
+            output += "<div id='divinfo"+field_header+"'>";
+            output += '<p><span class="column-title">'+selected['title']+'</span></p></span>'; 
+            output += '<p><span class="column-detail">'+selected['description']+'</span></p></span>'; 
+
+            var period_select = "<select id='selectperiod"+field_header+"'><option value='Now'> Now </option><option value='latest'> Latest  </option><option value=w> Week </option><option value=m> Month </option><option value=y> Year </option></select>";
+
+            if (selected['value_type'] == 'string') {
+
+                var values_select = "<p><select id='selectvalues"+field_header+"' MULTIPLE size=3>";
+
+                output += '<p>Values: ';
+
+                var values_list = selected['allowed_values'].split(",");
+
+                for (var value_index = 0; value_index < values_list.length ; value_index++) {
+                    var value_desc = values_list[value_index].split("-");
+                    if (value_index > 0)
+                        output += ', ';
+                    output += '<span class="bold">'+value_desc[0]+'</span>';
+                    values_select += "<option value ='"+value_desc[0]+"'>&nbsp;"+value_desc[0];
+                    if (value_desc[1]!='') 
+                        output += ' ('+value_desc[1]+')';
+
+                    values_select += "&nbsp;</option>";
+                }
+                values_select += "</select>";
+            }
+            else
+                output+='<p>Unit: '+selected['unit'];
+
+            output+= '</p>';
+
+            output += '<p>Source: <a class="source-url" target="source_window" href="'+selected['platform_url']+'">'+selected['platform']+'</a>';
+
+            //if (selected['via'] != '') 
+                //output += ' via <a class="source-url" target="source_window" href="http://'+selected['via_url']+'">'+selected['via']+'</a>';
+
+            output += '</p>';
+            output += "</div>";
+
+    /*
+            output += "<div id='divgroup"+field_header+"'>";
+            output += "<p>Group resources with the same value <input type=checkbox></input>";
+            output += "<p>Select aggregator : <select><option>Count</option><option selected=true>Average</option><option>Maximum</option><option>Minimum</option></select>";
+            output += "</div>";
+            output += "<div id='divtime"+field_header+"'>";
+            output += "<p>Select timestamp : ";
+            output += period_select;
+            output += "</div>";
+    */
+            output += "</div>";
+
+            return output;
+        },
+
+        update_autocomplete: function(availableTags)
+        {
+            var self = this;
+            var domid = this.options.plugin_uuid;
+            
+            jQuery.each(availableTags, function(key, value){
+                value.sort();
+                // using dataTables's $ to search also in nodes that are not currently displayed
+                var element = self.table.$("#"+domid+"__field__"+key);
+
+                element.autocomplete({
+                            source: value,
+                            selectFirst: true,
+                            minLength: 0, // allows to browse items with no value typed in
+                            select: self.event_filter_added('=')
+                });
+            });                
+        }, // update_autocomplete     
+
+/*
+        update_autocomplete: function(e, rows, current_query)
+        {
+            var d = data;
+            d.current_query = current_query;
+            var availableTags={};
+            jQuery.each (rows, function(index, obj) {                    
+                jQuery.each(obj,function(key,value){                       
+                    value = unfold.get_value(value); 
+                    if(!availableTags.hasOwnProperty(key)){availableTags[key]=new Array();}
+                    //availableTags[key].push(value);
+                    var currentArray=availableTags[key];
+                    if(value!=null){
+                        if(jQuery.inArray(value,currentArray)==-1){availableTags[key].push(value);}
+                    }
+                });                    
+            });
+            jQuery.each(availableTags, function(key, value){
+                value.sort();
+                jQuery("#"+options.plugin_uuid+"-filter-"+key).autocomplete({
+                            source: value,
+                            selectFirst: true,
+                            minLength: 0, // allows to browse items with no value typed in
+                            select: function(event, ui) {
+                                var key=getKeySplitId(this.id,"-");
+                                var op='=';
+                                var val=ui.item.value;
+                                
+                                query=d.current_query;
+                                query.update_filter(key,op,val);
+                                // Publish the query changed, the other plugins with subscribe will get the changes
+                                jQuery.publish('/query/' + query.uuid + '/changed', query);
+                                //add_ActiveFilter(this.id,'=',ui.item.value,d);
+                            }
+                });
+            });                
+        }, // update_autocomplete     
+*/
+        fnFormatDetails: function( metaTable, nTr, div_id ) 
+        {
+            var aData = metaTable.fnGetData( nTr );
+            var sOut = '<blockquote>';
+            //sOut += prepare_tab_description(aData[1].substr(21, aData[1].length-21-7), div_id);
+            sOut += this.print_field_description(aData[1].substring(3, aData[1].length-4), div_id);
+            sOut += '</blockquote>';
+         
+            return sOut;
+        }
+    });
+
+    $.plugin('ColumnsEditor', ColumnsEditor);
+
+})(jQuery);
diff --git a/plugins/columns_editor/templates/columns_editor.html b/plugins/columns_editor/templates/columns_editor.html
new file mode 100644 (file)
index 0000000..1d66e98
--- /dev/null
@@ -0,0 +1,45 @@
+<!--
+       $infopopup = <<<EOF
+<span class='info'><span> This tab allows you to customize the visible columns in the resource list, below. Note that information on the nodes comes from a variety of monitoring sources.  If you, as either a user or a provider of monitoring data, would like to see additional columns made available, please send us your request in mail to <a href='mailto:support@myslice.info'>support@myslice.info</a>. You can find more information about the MySlice project at <a href='http://trac.myslice.info'>http://trac.myslice.info</a>.  </span></span>
+EOF;
+
+           $filter_input = "<input id='filter_value' type=text size=5></input>";
+
+EOF; -->
+<div class='query-editor-spacer'>
+  <table id='{{domid}}__table' class='query-editor'>
+    <thead>
+      <tr>
+        <th class='center'>Info</th>
+        <th class='center'>Field</th>
+        <!--
+        <th class='center'>Resource</th>
+        <th class='center'>Type</th>
+        <th class='center'>Filter</th>
+        -->
+        <th class='center'>+/-</th>
+      </tr>
+    </thead>
+    <tbody>
+
+    {# Loop through metadata and display related information #}
+    {% for field in fields %}
+
+      <tr>
+        <td class='center'><span class='glyphicon glyphicon-chevron-right' style='cursor:pointer'></span></td>
+        <td class='center'>{{ field.name }}</td>
+        <!--
+        <td class='center'>{{ field.resource_type }}</td>
+        <td class='center'>{{ field.type }}</td>
+        <td class='center'>{{ field.filter_input }}</td>
+        -->
+        <td class='center'>
+          <input class='queryeditor-check' id='{{domid}}__check__{{ field.name }}' name='{{ field.header }}' type='checkbox' autocomplete='off' value='{{ field.name }}' {% if field.checked %} checked {% endif %}></input>
+        </td>
+      </tr>
+               
+       {% endfor %}
+
+    </tbody>
+  </table>
+</div>
diff --git a/plugins/columns_editor/templates/filter_input_integer.html b/plugins/columns_editor/templates/filter_input_integer.html
new file mode 100644 (file)
index 0000000..bdbacb6
--- /dev/null
@@ -0,0 +1,5 @@
+<div class="ui-widget content" style="margin-top:0px;">
+<label for='{{field}}'></label>
+<input class='queryeditor-filter-min' id='{{domid}}__field__{{field}}__min' type=text size=3 value='{{ min }}'></input>&nbsp;-
+<input class='queryeditor-filter-max' id='{{domid}}__field__{{field}}__max' type=text size=3 value='{{ max }}'></input>
+</div>
diff --git a/plugins/columns_editor/templates/filter_input_others.html b/plugins/columns_editor/templates/filter_input_others.html
new file mode 100644 (file)
index 0000000..14e6033
--- /dev/null
@@ -0,0 +1,4 @@
+<div class='ui-widget content' style='margin-top:0px;'>
+    <label for='{{field}}'></label>
+    <input class='queryeditor-auto-filter' id='{{domid}}__field__{{field}}' type='text' style='width:206px;'></input>
+</div>
diff --git a/plugins/columns_editor/templates/filter_input_string.html b/plugins/columns_editor/templates/filter_input_string.html
new file mode 100644 (file)
index 0000000..110f16d
--- /dev/null
@@ -0,0 +1,4 @@
+<div class='ui-widget content' style='margin-top:0px;'>
+  <label for='{{domid}}__field__{{field}}'></label>
+  <input class='queryeditor-auto-filter' id='{{domid}}__field__{{field}}' type='text' style='width:206px;'></input>
+</div>
diff --git a/plugins/columns_editor/templates/filter_input_string_values.html b/plugins/columns_editor/templates/filter_input_string_values.html
new file mode 100644 (file)
index 0000000..6a8aeca
--- /dev/null
@@ -0,0 +1,6 @@
+<select class='queryeditor-filter' id='$filter_id'>
+    <option value=''>Show all</option>
+       {% for option in options %}
+       <option>{{option}}</option>
+       {% endfor %}
+</select>
index 7d4ab08..254e2fd 100644 (file)
@@ -18,6 +18,7 @@ from plugins.googlemap               import GoogleMap
 from plugins.queryupdater            import QueryUpdater
 from plugins.testbeds                import TestbedsPlugin
 from plugins.scheduler2              import Scheduler2
+from plugins.columns_editor          import ColumnsEditor
 
 from myslice.theme import ThemeView
 
@@ -70,10 +71,8 @@ class SliceResourceView (LoginRequiredView, ThemeView):
         query_all_lease = Query.get('lease').select(lease_fields)
         page.enqueue_query(query_all_lease)
 
-        print "!!!!!!!!!!   query leases = ",query_all_lease
-
         # --------------------------------------------------------------------------
-        # RESOURCES LIST
+        # ALL RESOURCES LIST
         # resources as a list using datatable plugin
  
         list_resources = QueryTable(
@@ -91,6 +90,39 @@ class SliceResourceView (LoginRequiredView, ThemeView):
                 },
         )
 
+
+        # --------------------------------------------------------------------------
+        # RESERVED RESOURCES LIST
+        # resources as a list using datatable plugin
+        list_reserved_resources = QueryTable(
+            page       = page,
+            domid      = 'resources-reserved-list',
+            title      = 'List view',
+            query      = sq_resource,
+            query_all  = sq_resource,
+            init_key   = "urn",
+            checkboxes = True,
+            datatables_options = {
+                'iDisplayLength': 25,
+                'bLengthChange' : True,
+                'bAutoWidth'    : True,
+                },
+        )
+
+        # --------------------------------------------------------------------------
+        # COLUMNS EDITOR
+        # list of fields to be applied on the query 
+        # this will add/remove columns in QueryTable plugin
+        filter_column_editor = ColumnsEditor(
+            page  = page,
+            query = sq_resource, 
+            query_all = query_resource_all,
+            title = "Select Columns",
+            domid = 'select-columns',
+        )
+
         # --------------------------------------------------------------------------
         # RESOURCES MAP
         # the resources part is made of a Tabs (Geographic, List), 
@@ -170,6 +202,10 @@ class SliceResourceView (LoginRequiredView, ThemeView):
 
         template_env = {}
         template_env['list_resources'] = list_resources.render(self.request)
+        template_env['list_reserved_resources'] = list_reserved_resources.render(self.request)
+
+        template_env['columns_editor'] = filter_column_editor.render(self.request)
+
         template_env['filter_testbeds'] = filter_testbeds.render(self.request)
         template_env['map_resources'] = map_resources.render(self.request)
         template_env['scheduler'] = resources_as_scheduler2.render(self.request)
index 11a2017..89849ff 100644 (file)
@@ -17,7 +17,7 @@
                <div class="list-group-item list-resources">
                        <span class="list-group-item-heading">Resources</span>
                        <a class="list-group-item" data-panel="resources" href="#">All</a>
-                       <a class="list-group-item" data-panel="resources" href="#">Reserved</a>
+                       <a class="list-group-item" data-panel="reserved" href="#">Reserved</a>
                        <a class="list-group-item" data-panel="pending" href="#">Pending <span class="badge" id="badge-pending" data-number="0"></span></a>
                </div>
        
                          <li id="Scheduler"><a data-panel="scheduler-tab" href="#">Scheduler</a></li>
                        </ul>
                </div>
+               <div class="row" style="height:100%;display:none;">
+        {{columns_editor}}
+        </div>
                <div class="row" style="height:100%;">
                        <div id="resources" class="panel">
                 {{list_resources}}
                                <!-- <table cellpadding="0" cellspacing="0" border="0" class="table" id="objectList"></table> -->
                        </div>
+                       <div id="reserved" class="panel" style="height:370px;display:none;">
+                {{list_reserved_resources}}
+                       </div>
                        <div id="map" class="panel" style="height:370px;display:none;">
                 {{map_resources}}
                        </div>