diff --git a/server/src/uds/REST/mixins.py b/server/src/uds/REST/mixins.py index a484abda5..a97335831 100644 --- a/server/src/uds/REST/mixins.py +++ b/server/src/uds/REST/mixins.py @@ -34,6 +34,7 @@ from __future__ import unicode_literals from handlers import NotFound, RequestError from django.utils.translation import ugettext as _ +from django.db import IntegrityError import logging @@ -66,8 +67,14 @@ class ModelHandlerMixin(object): detail = None # Dictionary containing detail routing model = None save_fields = [] - + def __fillIntanceFields(self, item, res): + if hasattr(item, 'getInstance'): + for key, value in item.getInstance().valuesDict().iteritems(): + value = {"true":True, "false":False}.get(value, value) + logger.debug('{0} = {1}'.format(key, value)) + res[key] = value + def item_as_dict(self, item): pass @@ -96,9 +103,7 @@ class ModelHandlerMixin(object): result = [] for val in self.model.objects.all(): res = self.item_as_dict(val) - if hasattr(val, 'getInstance'): - for key, value in val.getInstance().valuesDict().iteritems(): - res[key] = value + self.__fillIntanceFields(val, res) result.append(res) return result @@ -109,14 +114,11 @@ class ModelHandlerMixin(object): if self.detail is not None and len(self._args) > 1: return self.processDetail() - try: val = self.model.objects.get(pk=self._args[0]) res = self.item_as_dict(val) - if hasattr(val, 'getInstance'): - for key, value in val.getInstance().valuesDict().iteritems(): - res[key] = value - return res + self.__fillIntanceFields(val, res) + return res except: raise NotFound('item not found') @@ -129,46 +131,49 @@ class ModelHandlerMixin(object): del self._params[key] except KeyError as e: raise RequestError('needed parameter not found in data {0}'.format(unicode(e))) - - if len(args) == 0: # create new - isNew = False - try: - item = self.model.objects.create(**args); - res = self.item_as_dict(item) - except: # Duplicate key probably - raise RequestError('Element already exists (duplicate key error)') - - elif len(args) == 1: - try: - item = self.model.objects.get(pk=self._args[0]); - # Update "general" values - item.update(**args) - res = self.item_as_dict(item) - except: - raise RequestError('Element {0} do not exists anymore'.format(self._args[0])) - else: - raise RequestError('incorrect invocation to PUT') try: - isNew = True - if self._params.has_key('data_type'): # Needs to store instance - item.data_type = self._params['data_type'] - item.data = item.getInstance(self._params).serialize() - - for key, value in item.getInstance().valuesDict().iteritems(): - res[key] = value - - item.save() + if len(self._args) == 0: # create new + item = self.model.objects.create(**args); + elif len(self._args) == 1: + # We have to take care with this case, update will efectively update records on db + item = self.model.objects.get(pk=self._args[0]); + item.__dict__.update(args) # Update fields from args + else: + raise Exception() # Incorrect invocation + except self.model.DoesNotExist: + raise NotFound('Element do not exists') + except IntegrityError: # Duplicate key probably + raise RequestError('Element already exists (duplicate key error)') except Exception as e: - item.delete() # Remove pre-saved element - raise RequestError(unicode(e)) + raise RequestError('incorrect invocation to PUT') + + # Store associated object if needed + if self._params.has_key('data_type'): # Needs to store instance + item.data_type = self._params['data_type'] + item.data = item.getInstance(self._params).serialize() + + res = self.item_as_dict(item) + self.__fillIntanceFields(item, res) + + item.save() + return res def delete(self): logger.debug('method DELETE for {0}, {1}'.format(self.__class__.__name__, self._args)) if len(self._args) != 1: - raise RequestError('Delete need an argument') + raise RequestError('Delete need one and only one argument') + try: + item = self.model.objects.get(pk=self._args[0]); + item.delete() + except self.model.DoesNotExist: + raise NotFound('Element do not exists') + except Exception as e: + logger.exception('delete') + raise RequestError('incorrect invocation to DELETE') + return 'deleted' class ModelTypeHandlerMixin(object): diff --git a/server/src/uds/static/adm/js/api.js b/server/src/uds/static/adm/js/api.js index 90c1b8048..4287b7024 100644 --- a/server/src/uds/static/adm/js/api.js +++ b/server/src/uds/static/adm/js/api.js @@ -96,7 +96,35 @@ request.setRequestHeader(api.config.auth_header, api.config.token); }, }); - }; + }; // End putJson + + + api.deleteJson = function(path, options) { + options = options || {}; + var success_fnc = options.success || function(){}; + var fail_fnc = options.fail || api.defaultFail; + + var url = api.url_for(path); + api.doLog('Ajax DELETE Json for "' + url + '"'); + $.ajax({ + url : url, + type : "DELETE", + dataType : "json", + success: function(data) { + api.doLog('Success on DELETE "' + url + '".'); + api.doLog('Received ', data); + success_fnc(data); + }, + error: function(jqXHR, textStatus, errorThrown) { + api.doLog('Error on DELETE "' + url + '". ', textStatus, ', ', errorThrown); + fail_fnc(jqXHR, textStatus, errorThrown); + }, + beforeSend : function(request) { + request.setRequestHeader(api.config.auth_header, api.config.token); + }, + }); + }; // End putJson + // Public attributes api.debug = true; @@ -142,6 +170,7 @@ function BasicModelRest(path, options) { this.path = path; this.getPath = options.getPath || path; this.putPath = options.putPath || path; + this.delPath = options.delPath || path; this.typesPath = options.typesPath || (path + '/types'); this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo'); this.cache = api.cache('bmr'+path); @@ -254,6 +283,20 @@ BasicModelRest.prototype = { }); }, + // -------------- + // Delete + // -------------- + del: function(id, success_fnc, fail_fnc) { + "use strict"; + + var path = this.delPath + '/' + id; + + api.deleteJson(path, { + success: success_fnc, + fail: fail_fnc + }); + }, + // -------------- // Types methods // -------------- diff --git a/server/src/uds/static/adm/js/gui-definition.js b/server/src/uds/static/adm/js/gui-definition.js index 8ae2d813b..5e7cfe263 100644 --- a/server/src/uds/static/adm/js/gui-definition.js +++ b/server/src/uds/static/adm/js/gui-definition.js @@ -139,15 +139,22 @@ gui.connectivity.link = function(event) { })); gui.connectivity.transports.table({ - rowSelect : 'multi', + rowSelect : 'single', container : 'transports-placeholder', buttons : [ 'new', 'edit', 'delete', 'xls' ], - onEdit: function(value, event, table) { + onEdit: function(value, event, table, refreshFnc) { gui.connectivity.transports.rest.gui(value.type, function(itemGui){ gui.connectivity.transports.rest.item(value.id, function(item) { - var form = gui.fields(itemGui, item); - gui.launchModalForm(gettext('Edit transport')+' '+value.name,form, function(form_selector) { - var fields = gui.fields.read(form_selector); + var form = gui.form.fromFields(itemGui, item); + gui.launchModalForm(gettext('Edit transport')+' '+value.name,form, function(form_selector, closeFnc) { + var fields = gui.form.read(form_selector); + fields.data_type = value.type; + fields.nets_positive = false; + gui.connectivity.transports.rest.save(fields, function(data) { // Success on put + closeFnc(); + refreshFnc(); + }, gui.failRequestModalFnc(gettext('Error creating transport')) // Fail on put, show modal message + ); return false; }); }); @@ -155,9 +162,9 @@ gui.connectivity.link = function(event) { }, onNew: function(type, table, refreshFnc) { gui.connectivity.transports.rest.gui(type, function(itemGui) { - var form = gui.fields(itemGui); + var form = gui.form.fromFields(itemGui); gui.launchModalForm(gettext('New transport'), form, function(form_selector, closeFnc) { - var fields = gui.fields.read(form_selector); + var fields = gui.form.read(form_selector); // Append "own" fields, in this case data_type fields.data_type = type; fields.nets_positive = false; @@ -169,6 +176,13 @@ gui.connectivity.link = function(event) { }); }); }, + onDelete: function(value, event, table, refreshFnc) { + // TODO: Add confirmation to deletion + gui.connectivity.transports.rest.del(value.id, function(){ + refreshFnc(); + }, gui.failRequestModalFnc(gettext('Error removing transport')) + ); + }, }); gui.connectivity.networks.table({ rowSelect : 'multi', diff --git a/server/src/uds/static/adm/js/gui-element.js b/server/src/uds/static/adm/js/gui-element.js index 72ea9e1f1..6645b20d9 100644 --- a/server/src/uds/static/adm/js/gui-element.js +++ b/server/src/uds/static/adm/js/gui-element.js @@ -19,12 +19,12 @@ GuiElement.prototype = { init : function() { "use strict"; gui.doLog('Initializing ' + this.name); - var $this = this; + var self = this; this.rest.types(function(data) { var styles = ''; $.each(data, function(index, value) { - var className = $this.name + '-' + value.type; - $this.types[value.type] = { + var className = self.name + '-' + value.type; + self.types[value.type] = { css : className, name : value.name || '', description : value.description || '' @@ -78,7 +78,7 @@ GuiElement.prototype = { options = options || {}; gui.doLog('Composing table for ' + this.name); var tableId = this.name + '-table'; - var $this = this; // Store this for child functions + var self = this; // Store this for child functions // --------------- // Cells renderers @@ -101,7 +101,7 @@ GuiElement.prototype = { // Icon renderer, based on type (created on init methods in styles) var renderTypeIcon = function(data, type, value){ if( type == 'display' ) { - var css = $this.types[value.type].css; + var css = self.types[value.type].css; return ' ' + renderEmptyCell(data); } else { return renderEmptyCell(data); @@ -185,7 +185,7 @@ GuiElement.prototype = { columns: columns, })).appendTo('head'); - $this.rest.overview(function(data) { // Gets "overview" data for table (table contents, but resume form) + self.rest.overview(function(data) { // Gets "overview" data for table (table contents, but resume form) var table = gui.table(title, tableId); if (options.container === undefined) { gui.appendToWorkspace('
' + table.text + '
'); @@ -205,11 +205,11 @@ GuiElement.prototype = { if( data.length > 1000 ) api.tools.blockUI(); - $this.rest.overview(function(data) { // Restore overview + self.rest.overview(function(data) { // Restore overview setTimeout( function() { tbl.fnClearTable(); tbl.fnAddData(data); - onRefresh($this); + onRefresh(self); api.tools.unblockUI(); }, 0); }); // End restore overview @@ -257,7 +257,7 @@ GuiElement.prototype = { var btn; switch (value) { case 'new': - if(Object.keys($this.types).length === 0) { + if(Object.keys(self.types).length === 0) { btn = { "sExtends" : "text", "sButtonText" : gui.config.dataTableButtons['new'].text, @@ -269,7 +269,7 @@ GuiElement.prototype = { var newButtons = []; // Order buttons by name, much more easy for users... :-) var order = []; - $.each($this.types, function(k, v){ + $.each(self.types, function(k, v){ order.push({ type: k, css: v.css, @@ -424,7 +424,7 @@ GuiElement.prototype = { } // if table rendered event if( options.onLoad ) { - options.onLoad($this); + options.onLoad(self); } }); // End Overview data }); // End Tableinfo data diff --git a/server/src/uds/static/adm/js/gui-fields.js b/server/src/uds/static/adm/js/gui-form.js similarity index 82% rename from server/src/uds/static/adm/js/gui-fields.js rename to server/src/uds/static/adm/js/gui-form.js index 3233a16b9..ccdf04367 100644 --- a/server/src/uds/static/adm/js/gui-fields.js +++ b/server/src/uds/static/adm/js/gui-form.js @@ -3,11 +3,8 @@ "use strict"; // Returns a form that will manage a gui description (new or edit) - gui.fields = function(itemGui, item) { - var editing = item !== undefined; // Locate real Editing - item = item || {id:''}; - var form = '
' + - ''; + gui.fieldsToHtml = function(itemGui, item, editing) { + var html = ''; // itemGui is expected to have fields sorted by .gui.order (REST api returns them sorted) $.each(itemGui, function(index, f){ gui.doLog(f); @@ -16,7 +13,7 @@ if( f.gui.type == 'text' && f.gui.multiline ) { f.gui.type = 'textbox'; } - form += api.templates.evaluate('tmpl_fld_'+f.gui.type, { + html += api.templates.evaluate('tmpl_fld_'+f.gui.type, { value: item[f.name] || f.gui.value || f.gui.defvalue, // If no value present, use default value values: f.gui.values, label: f.gui.label, @@ -30,12 +27,27 @@ css: 'modal_field_data', }); }); + return html; + }; + + gui.form = {}; + + gui.form.fromFields = function(fields, item) { + var editing = item !== undefined; // Locate real Editing + item = item || {id:''}; + var form = '' + + ''; + if( fields.tabs ) { + + } else { + form += gui.fieldsToHtml(fields, item, editing); + } form += '
'; return form; }; - + // Reads fields from a form - gui.fields.read = function(formSelector) { + gui.form.read = function(formSelector) { var res = {}; $(formSelector + ' .modal_field_data').each(function(i, field) { var $field = $(field); @@ -51,5 +63,6 @@ return res; }; + }(window.gui = window.gui || {}, jQuery)); diff --git a/server/src/uds/static/adm/js/gui.js b/server/src/uds/static/adm/js/gui.js index 018ffecf8..0486416dc 100644 --- a/server/src/uds/static/adm/js/gui.js +++ b/server/src/uds/static/adm/js/gui.js @@ -112,15 +112,15 @@ gui.appendToWorkspace(gui.modal(id, title, content, actionButton, closeButton)); id = '#' + id; // for jQuery - $(id).modal({keyboard: false}) + $(id).modal() .on('hidden.bs.modal', function () { $(id).remove(); }); }; - gui.launchModalForm = function(title, content, onSuccess) { + gui.launchModalForm = function(title, form, onSuccess) { var id = Math.random().toString().split('.')[1]; // Get a random ID for this modal - gui.appendToWorkspace(gui.modal(id, title, content)); + gui.appendToWorkspace(gui.modal(id, title, form)); id = '#' + id; // for jQuery // Get form diff --git a/server/src/uds/templates/uds/admin/index.html b/server/src/uds/templates/uds/admin/index.html index 183b726dc..49c46db60 100644 --- a/server/src/uds/templates/uds/admin/index.html +++ b/server/src/uds/templates/uds/admin/index.html @@ -98,7 +98,7 @@ - +