mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-22 13:34:04 +03:00
A bit of refactoring with api, gui, etc...
solved some problems with PUT methods (need to solve a few ones anyway) Create authenticator now works, but needs more elaboration lots of changes i event remember.. :-)
This commit is contained in:
parent
a52e8dc548
commit
8c04c88d86
@ -37,7 +37,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _, activate
|
||||
from django.conf import settings
|
||||
from handlers import Handler, HandlerError, AccessDenied, NotFound
|
||||
from handlers import Handler, HandlerError, AccessDenied, NotFound, RequestError
|
||||
|
||||
import time
|
||||
import logging
|
||||
@ -148,12 +148,14 @@ class Dispatcher(View):
|
||||
for k, v in handler.headers().iteritems():
|
||||
response[k] = v
|
||||
return response
|
||||
except HandlerError as e:
|
||||
return http.HttpResponseBadRequest(unicode(e))
|
||||
except RequestError as e:
|
||||
return http.HttpResponseServerError(unicode(e))
|
||||
except AccessDenied as e:
|
||||
return http.HttpResponseForbidden(unicode(e))
|
||||
except NotFound as e:
|
||||
return http.Http404(unicode(e))
|
||||
except HandlerError as e:
|
||||
return http.HttpResponseBadRequest(unicode(e))
|
||||
except Exception as e:
|
||||
logger.exception('Error processing request')
|
||||
return http.HttpResponseServerError(unicode(e))
|
||||
|
@ -32,7 +32,6 @@
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.sessions.backends.db import SessionStore
|
||||
from django.utils.translation import activate
|
||||
from django.conf import settings
|
||||
|
||||
from uds.core.util.Config import GlobalConfig
|
||||
@ -52,6 +51,9 @@ class NotFound(HandlerError):
|
||||
class AccessDenied(HandlerError):
|
||||
pass
|
||||
|
||||
class RequestError(HandlerError):
|
||||
pass
|
||||
|
||||
class Handler(object):
|
||||
raw = False # If true, Handler will return directly an HttpResponse Object
|
||||
name = None # If name is not used, name will be the class name in lower case
|
||||
|
@ -32,7 +32,7 @@
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||
from uds.models import Transport
|
||||
from uds.core.transports import factory
|
||||
|
||||
@ -47,6 +47,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class Transports(ModelHandlerMixin, Handler):
|
||||
model = Transport
|
||||
save_fields = ['name', 'comments', 'priority', 'nets_positive']
|
||||
|
||||
def item_as_dict(self, item):
|
||||
type_ = item.getType()
|
||||
@ -58,16 +59,27 @@ class Transports(ModelHandlerMixin, Handler):
|
||||
'deployed_count': item.deployedServices.count(),
|
||||
'type': type_.type(),
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Types(ModelTypeHandlerMixin, Handler):
|
||||
path = 'transports'
|
||||
has_comments = True
|
||||
|
||||
def enum_types(self):
|
||||
return factory().providers().values()
|
||||
|
||||
def getGui(self, type_):
|
||||
try:
|
||||
return factory().lookup(type_).guiDescription()
|
||||
return self.addField(self.addDefaultFields(factory().lookup(type_).guiDescription(), ['name', 'comments']), {
|
||||
'name': 'priority',
|
||||
'required': True,
|
||||
'value': '1',
|
||||
'label': ugettext('Priority'),
|
||||
'tooltip': ugettext('Priority of this transport'),
|
||||
'type': 'numeric',
|
||||
'order': 100, # At end
|
||||
})
|
||||
except:
|
||||
raise NotFound('type not found')
|
||||
|
||||
@ -78,5 +90,6 @@ class TableInfo(ModelTableHandlerMixin, Handler):
|
||||
fields = [
|
||||
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
||||
{ 'comments': {'title': _('Comments')}},
|
||||
{ 'priority': {'title': _('Priority'), 'type': 'numeric', 'width': '6em' }},
|
||||
{ 'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
||||
]
|
||||
|
@ -32,7 +32,7 @@
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from handlers import NotFound
|
||||
from handlers import NotFound, RequestError
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
import logging
|
||||
@ -65,6 +65,8 @@ class ModelHandlerMixin(object):
|
||||
needs_staff = True
|
||||
detail = None # Dictionary containing detail routing
|
||||
model = None
|
||||
save_fields = []
|
||||
|
||||
|
||||
def item_as_dict(self, item):
|
||||
pass
|
||||
@ -117,6 +119,37 @@ class ModelHandlerMixin(object):
|
||||
return res
|
||||
except:
|
||||
raise NotFound('item not found')
|
||||
|
||||
def put(self):
|
||||
logger.debug('method PUT for {0}, {1}'.format(self.__class__.__name__, self._args))
|
||||
args = {}
|
||||
try:
|
||||
for key in self.save_fields:
|
||||
args[key] = self._params[key]
|
||||
del self._params[key]
|
||||
except KeyError as e:
|
||||
raise RequestError('needed parameter not found in data {0}'.format(unicode(e)))
|
||||
try:
|
||||
item = self.model.objects.create(**args);
|
||||
except: # Duplicate key probably
|
||||
raise RequestError('Element already exists (duplicate key error)')
|
||||
|
||||
try:
|
||||
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()
|
||||
item.save()
|
||||
except Exception as e:
|
||||
item.delete() # Remove pre-saved element
|
||||
raise RequestError(unicode(e))
|
||||
|
||||
return {'id': item.id }
|
||||
|
||||
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')
|
||||
return 'deleted'
|
||||
|
||||
class ModelTypeHandlerMixin(object):
|
||||
'''
|
||||
@ -125,6 +158,7 @@ class ModelTypeHandlerMixin(object):
|
||||
'''
|
||||
authenticated = True
|
||||
needs_staff = True
|
||||
has_comments = False
|
||||
|
||||
def enum_types(self):
|
||||
pass
|
||||
@ -140,6 +174,46 @@ class ModelTypeHandlerMixin(object):
|
||||
for type_ in self.enum_types():
|
||||
yield self.type_as_dict(type_)
|
||||
|
||||
def addField(self, gui, field):
|
||||
gui.append({
|
||||
'name': field.get('name', ''),
|
||||
'value': '',
|
||||
'gui': {
|
||||
'required': field.get('required', False),
|
||||
'defvalue': field.get('value', ''),
|
||||
'value': field.get('value', ''),
|
||||
'label': field.get('label', ''),
|
||||
'length': field.get('length', 128),
|
||||
'multiline': field.get('multiline', 0),
|
||||
'tooltip': field.get('tooltip', ''),
|
||||
'rdonly': field.get('rdonly', False),
|
||||
'type': field.get('type', 'text'),
|
||||
'order': field.get('order', 0),
|
||||
'values': field.get('values', [])
|
||||
}
|
||||
})
|
||||
return gui
|
||||
|
||||
def addDefaultFields(self, gui, flds):
|
||||
if 'name' in flds:
|
||||
self.addField(gui, {
|
||||
'name': 'name',
|
||||
'required': True,
|
||||
'label': _('Name'),
|
||||
'tooltip': _('Name of this element'),
|
||||
'order': -2,
|
||||
})
|
||||
# And maybe comments (only if model has this field)
|
||||
if 'comments' in flds:
|
||||
self.addField(gui, {
|
||||
'name': 'comments',
|
||||
'label': _('Comments'),
|
||||
'tooltip': _('Comments for this element'),
|
||||
'length': 256,
|
||||
'order': -1,
|
||||
})
|
||||
return gui
|
||||
|
||||
def get(self):
|
||||
logger.debug(self._args)
|
||||
nArgs = len(self._args)
|
||||
@ -162,42 +236,7 @@ class ModelTypeHandlerMixin(object):
|
||||
if self._args[1] == 'gui':
|
||||
gui = self.getGui(self._args[0])
|
||||
# Add name default description, at top of form
|
||||
gui.append({
|
||||
'name': 'name',
|
||||
'value':'',
|
||||
'gui': {
|
||||
'required':True,
|
||||
'defvalue':'',
|
||||
'value':'',
|
||||
'label': _('Name'),
|
||||
'length': 128,
|
||||
'multiline': 0,
|
||||
'tooltip': _('Name of this element'),
|
||||
'rdonly': False,
|
||||
'type': 'text',
|
||||
'order': -2
|
||||
}
|
||||
})
|
||||
# And comments
|
||||
gui.append({
|
||||
'name': 'comments',
|
||||
'value':'',
|
||||
'gui': {
|
||||
'required':False,
|
||||
'defvalue':'',
|
||||
'value':'',
|
||||
'label': _('Comments'),
|
||||
'length': 256,
|
||||
'multiline': 0,
|
||||
'tooltip': _('Comments for this element'),
|
||||
'rdonly': False,
|
||||
'type': 'text',
|
||||
'order': -1
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
logger.debug("GUI: {0}".format(gui))
|
||||
return sorted(gui, key=lambda f: f['gui']['order']);
|
||||
|
||||
|
@ -125,7 +125,9 @@ class gui(object):
|
||||
Returns:
|
||||
True if the string is "true" (case insensitive), False else.
|
||||
'''
|
||||
if str_.lower() == gui.TRUE:
|
||||
if isinstance(str_, bool):
|
||||
return str_
|
||||
if unicode(str_).lower() == gui.TRUE:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -40,9 +40,15 @@
|
||||
}
|
||||
};
|
||||
|
||||
// Default fail function
|
||||
api.defaultFail = function(jqXHR, textStatus, errorThrown) {
|
||||
api.doLog(jqXHR, ', ', textStatus, ', ', errorThrown);
|
||||
};
|
||||
|
||||
api.getJson = function(path, options) {
|
||||
options = options || {};
|
||||
var success_fnc = options.success || function(){};
|
||||
var success_fnc = options.success || function(){};
|
||||
var fail_fnc = options.fail || api.defaultFail;
|
||||
|
||||
var url = api.url_for(path);
|
||||
api.doLog('Ajax GET Json for "' + url + '"');
|
||||
@ -51,10 +57,41 @@
|
||||
type : "GET",
|
||||
dataType : "json",
|
||||
success : function(data) {
|
||||
api.doLog('Success on "' + url + '".');
|
||||
api.doLog('Received ' + JSON.stringify(data));
|
||||
api.doLog('Success on GET "' + url + '".');
|
||||
api.doLog('Received ', data);
|
||||
success_fnc(data);
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
api.doLog('Error on GET "' + url + '". ', textStatus, ', ', errorThrown);
|
||||
fail_fnc(jqXHR, textStatus, errorThrown);
|
||||
},
|
||||
beforeSend : function(request) {
|
||||
request.setRequestHeader(api.config.auth_header, api.config.token);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
api.putJson = function(path, data, 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 PUT Json for "' + url + '"');
|
||||
$.ajax({
|
||||
url : url,
|
||||
type : "PUT",
|
||||
dataType : "json",
|
||||
data: JSON.stringify(data),
|
||||
success: function(data) {
|
||||
api.doLog('Success on PUT "' + url + '".');
|
||||
api.doLog('Received ', data);
|
||||
success_fnc(data);
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
api.doLog('Error on PUT "' + url + '". ', textStatus, ', ', errorThrown);
|
||||
fail_fnc(jqXHR, textStatus, errorThrown);
|
||||
},
|
||||
beforeSend : function(request) {
|
||||
request.setRequestHeader(api.config.auth_header, api.config.token);
|
||||
},
|
||||
@ -62,7 +99,7 @@
|
||||
};
|
||||
|
||||
// Public attributes
|
||||
api.debug = false;
|
||||
api.debug = true;
|
||||
}(window.api = window.api || {}, jQuery));
|
||||
|
||||
|
||||
@ -104,6 +141,7 @@ function BasicModelRest(path, options) {
|
||||
// Requests paths
|
||||
this.path = path;
|
||||
this.getPath = options.getPath || path;
|
||||
this.putPath = options.putPath || path;
|
||||
this.typesPath = options.typesPath || (path + '/types');
|
||||
this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo');
|
||||
this.cache = api.cache('bmr'+path);
|
||||
@ -117,7 +155,8 @@ BasicModelRest.prototype = {
|
||||
_requestPath: function(path, options) {
|
||||
"use strict";
|
||||
options = options || {};
|
||||
var success_fnc = options.success || function(){api.doLog('success not provided for '+path);};
|
||||
var success_fnc = options.success || function(){api.doLog('success function not provided for '+path);};
|
||||
var fail_fnc = options.fail;
|
||||
var cacheKey = options.cacheKey || path;
|
||||
|
||||
if( path == '.' ) {
|
||||
@ -136,10 +175,11 @@ BasicModelRest.prototype = {
|
||||
}
|
||||
success_fnc(data);
|
||||
},
|
||||
fail: fail_fnc,
|
||||
});
|
||||
}
|
||||
},
|
||||
get : function(success_fnc, options) {
|
||||
get: function(options) {
|
||||
"use strict";
|
||||
options = options || {};
|
||||
|
||||
@ -148,61 +188,103 @@ BasicModelRest.prototype = {
|
||||
path += '/' + options.id;
|
||||
return this._requestPath(path, {
|
||||
cacheKey: '.', // Right now, do not cache any "get" method
|
||||
success: success_fnc,
|
||||
success: options.success,
|
||||
fail: options.fail
|
||||
|
||||
});
|
||||
},
|
||||
list: function(success_fnc, options) { // This is "almost" an alias for get
|
||||
list: function(success_fnc, fail_fnc) { // This is "almost" an alias for get
|
||||
"use strict";
|
||||
options = options || {};
|
||||
return this.get(success_fnc, {
|
||||
return this.get({
|
||||
id: '',
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
},
|
||||
overview: function(success_fnc, options) {
|
||||
overview: function(success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
options = options || {};
|
||||
return this.get(success_fnc, {
|
||||
return this.get({
|
||||
id: 'overview',
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
},
|
||||
item: function(itemId, success_fnc, options) {
|
||||
item: function(itemId, success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
options = options || {};
|
||||
return this.get(success_fnc, {
|
||||
return this.get({
|
||||
id: itemId,
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
|
||||
},
|
||||
types : function(success_fnc, options) {
|
||||
|
||||
// -------------
|
||||
// Put methods
|
||||
// -------------
|
||||
|
||||
put: function(data, options) {
|
||||
"use strict";
|
||||
options = options || {};
|
||||
|
||||
var path = this.putPath;
|
||||
if ( options.id )
|
||||
path += '/' + options.id;
|
||||
|
||||
api.putJson(path, data, {
|
||||
success: options.success,
|
||||
fail: options.fail
|
||||
});
|
||||
},
|
||||
create: function(data, success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
|
||||
return this.put(data, {
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
},
|
||||
save: function(data, success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
|
||||
return this.put(data, {
|
||||
id: data.id,
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
},
|
||||
|
||||
// --------------
|
||||
// Types methods
|
||||
// --------------
|
||||
types : function(success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
return this._requestPath(this.typesPath, {
|
||||
cacheKey: 'type',
|
||||
success: success_fnc,
|
||||
});
|
||||
},
|
||||
gui: function(typeName, success_fnc, options) {
|
||||
gui: function(typeName, success_fnc, fail_fnc) {
|
||||
// GUI returns a dict, that contains:
|
||||
// name: Name of the field
|
||||
// value: value of the field (selected element in choice, text for inputs, etc....)
|
||||
// gui: Description of the field (type, value or values, defvalue, ....
|
||||
"use strict";
|
||||
options = options || {};
|
||||
var path = [this.typesPath, typeName, 'gui'].join('/');
|
||||
return this._requestPath(path, {
|
||||
cacheKey: typeName + '-gui',
|
||||
success: success_fnc,
|
||||
fail: fail_fnc,
|
||||
});
|
||||
},
|
||||
tableInfo : function(success_fnc, options) {
|
||||
tableInfo : function(success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
options = options || {};
|
||||
success_fnc = success_fnc || function(){api.doLog('success not provided for tableInfo');};
|
||||
|
||||
var path = this.tableInfoPath;
|
||||
this._requestPath(path, {
|
||||
success: success_fnc,
|
||||
fail: fail_fnc,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -153,12 +153,20 @@ gui.connectivity.link = function(event) {
|
||||
});
|
||||
});
|
||||
},
|
||||
onNew: function(type) {
|
||||
onNew: function(type, table, refreshFnc) {
|
||||
gui.connectivity.transports.rest.gui(type, function(itemGui) {
|
||||
var form = gui.fields(itemGui);
|
||||
gui.launchModalForm(gettext('New transport'), form, function(form_selector) {
|
||||
gui.launchModalForm(gettext('New transport'), form, function(form_selector, closeFnc) {
|
||||
var fields = gui.fields.read(form_selector);
|
||||
return false;
|
||||
// Append "own" fields, in this case data_type
|
||||
fields.data_type = type;
|
||||
fields.nets_positive = false;
|
||||
gui.connectivity.transports.rest.create(fields, function(data) { // Success on put
|
||||
closeFnc();
|
||||
refreshFnc();
|
||||
}, function(jqXHR, textStatus, errorThrown) { // fail on put
|
||||
gui.launchModal(gettext('Error creating transport'), jqXHR.responseText, ' ');
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
428
server/src/uds/static/adm/js/gui-element.js
Normal file
428
server/src/uds/static/adm/js/gui-element.js
Normal file
@ -0,0 +1,428 @@
|
||||
/* jshint strict: true */
|
||||
function BasicGuiElement(name) {
|
||||
"use strict";
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
function GuiElement(restItem, name) {
|
||||
"use strict";
|
||||
this.rest = restItem;
|
||||
this.name = name;
|
||||
this.types = {};
|
||||
this.init();
|
||||
}
|
||||
|
||||
// all gui elements has, at least, name && type
|
||||
// Types must include, at least: type, icon
|
||||
GuiElement.prototype = {
|
||||
init : function() {
|
||||
"use strict";
|
||||
gui.doLog('Initializing ' + this.name);
|
||||
var $this = this;
|
||||
this.rest.types(function(data) {
|
||||
var styles = '';
|
||||
$.each(data, function(index, value) {
|
||||
var className = $this.name + '-' + value.type;
|
||||
$this.types[value.type] = {
|
||||
css : className,
|
||||
name : value.name || '',
|
||||
description : value.description || ''
|
||||
};
|
||||
gui.doLog('Creating style for ' + className);
|
||||
var style = '.' + className + ' { display:inline-block; background: url(data:image/png;base64,' +
|
||||
value.icon + '); ' + 'width: 16px; height: 16px; vertical-align: middle; } ';
|
||||
styles += style;
|
||||
});
|
||||
if (styles !== '') {
|
||||
styles = '<style media="screen">' + styles + '</style>';
|
||||
$(styles).appendTo('head');
|
||||
}
|
||||
});
|
||||
},
|
||||
// Options: dictionary
|
||||
// container: container ID of parent for the table. If undefined, table will be appended to workspace
|
||||
// buttons: array of visible buttons (strings), valid are [ 'new', 'edit', 'refresh', 'delete', 'xls' ],
|
||||
// rowSelect: type of allowed row selection, valid values are 'single' and 'multi'
|
||||
// scrollToTable: if True, will scroll page to show table
|
||||
//
|
||||
// onLoad: Event (function). If defined, will be invoked when table is fully loaded.
|
||||
// Receives 1 parameter, that is the gui element (GuiElement) used to render table
|
||||
// onRowSelect: Event (function). If defined, will be invoked when a row of table is selected
|
||||
// Receives 3 parameters:
|
||||
// 1.- the array of selected items data (objects, as got from api...get)
|
||||
// 2.- the DataTable that raised the event
|
||||
// 3.- the DataTableTools that raised the event
|
||||
// onRowDeselect: Event (function). If defined, will be invoked when a row of table is deselected
|
||||
// Receives 3 parameters:
|
||||
// 1.- the array of selected items data (objects, as got from api...get)
|
||||
// 2.- the DataTable that raised the event
|
||||
// onNew: Event (function). If defined, will be invoked when "new" button is pressed
|
||||
// Receives 4 parameters:
|
||||
// 1.- the selected item data (single object, as got from api...get)
|
||||
// 2.- the event that fired this (new, delete, edit, ..)
|
||||
// 3.- the DataTable that raised the event
|
||||
// onEdit: Event (function). If defined, will be invoked when "edit" button is pressed
|
||||
// Receives 4 parameters:
|
||||
// 1.- the selected item data (single object, as got from api...get)
|
||||
// 2.- the event that fired this (new, delete, edit, ..)
|
||||
// 3.- the DataTable that raised the event
|
||||
// onDelete: Event (function). If defined, will be invoked when "delete" button is pressed
|
||||
// Receives 4 parameters:
|
||||
// 1.- the selected item data (single object, as got from api...get)
|
||||
// 2.- the event that fired this (new, delete, edit, ..)
|
||||
// 4.- the DataTable that raised the event
|
||||
table : function(options) {
|
||||
"use strict";
|
||||
gui.doLog('Types: ', this.types);
|
||||
options = options || {};
|
||||
gui.doLog('Composing table for ' + this.name);
|
||||
var tableId = this.name + '-table';
|
||||
var $this = this; // Store this for child functions
|
||||
|
||||
// Empty cells transform
|
||||
var renderEmptyCell = function(data) {
|
||||
if( data === '' )
|
||||
return '-';
|
||||
return data;
|
||||
};
|
||||
|
||||
// Datetime renderer (with specified format)
|
||||
var renderDate = function(format) {
|
||||
return function(data, type, full) {
|
||||
return api.tools.strftime(format, new Date(data*1000));
|
||||
};
|
||||
};
|
||||
|
||||
// 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;
|
||||
return '<span class="' + css + '"></span> ' + renderEmptyCell(data);
|
||||
} else {
|
||||
return renderEmptyCell(data);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom icon renderer, in fact span with defined class
|
||||
var renderIcon = function(icon) {
|
||||
return function(data, type, full) {
|
||||
if( type == 'display' ) {
|
||||
return '<span class="' + icon + '"></span> ' + renderEmptyCell(data);
|
||||
} else {
|
||||
return renderEmptyCell(data);
|
||||
}
|
||||
};
|
||||
};
|
||||
// Text transformation, dictionary based
|
||||
var renderTextTransform = function(dict) {
|
||||
return function(data, type, full) {
|
||||
return dict[data] || renderEmptyCell('');
|
||||
};
|
||||
};
|
||||
this.rest.tableInfo(function(data) {
|
||||
var title = data.title;
|
||||
var columns = [];
|
||||
$.each(data.fields, function(index, value) {
|
||||
for ( var v in value) {
|
||||
var opts = value[v];
|
||||
var column = {
|
||||
mData : v,
|
||||
};
|
||||
column.sTitle = opts.title;
|
||||
column.mRender = renderEmptyCell;
|
||||
if (opts.width)
|
||||
column.sWidth = opts.width;
|
||||
column.bVisible = opts.visible === undefined ? true : opts.visible;
|
||||
if (opts.sortable !== undefined)
|
||||
column.bSortable = opts.sortable;
|
||||
if (opts.searchable !== undefined)
|
||||
column.bSearchable = opts.searchable;
|
||||
|
||||
if (opts.type && column.bVisible ) {
|
||||
switch(opts.type) {
|
||||
case 'date':
|
||||
column.sType = 'date';
|
||||
column.mRender = renderDate(api.tools.djangoFormat(get_format('SHORT_DATE_FORMAT')));
|
||||
break;
|
||||
case 'datetime':
|
||||
column.sType = 'date';
|
||||
column.mRender = renderDate(api.tools.djangoFormat(get_format('SHORT_DATETIME_FORMAT')));
|
||||
break;
|
||||
case 'time':
|
||||
column.mRender = renderDate(api.tools.djangoFormat(get_format('TIME_FORMAT')));
|
||||
break;
|
||||
case 'iconType':
|
||||
//columnt.sType = 'html'; // html is default, so this is not needed
|
||||
column.mRender = renderTypeIcon;
|
||||
break;
|
||||
case 'icon':
|
||||
if( opts.icon !== undefined ) {
|
||||
column.mRender = renderIcon(opts.icon);
|
||||
}
|
||||
break;
|
||||
case 'dict':
|
||||
if( opts.dict !== undefined ) {
|
||||
column.mRender = renderTextTransform(opts.dict);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
column.sType = opts.type;
|
||||
}
|
||||
}
|
||||
columns.push(column);
|
||||
}
|
||||
});
|
||||
// Responsive style for tables, using tables.css and this code generates the "titles" for vertical display on small sizes
|
||||
$('#style-' + tableId).remove(); // Remove existing style for table before adding new one
|
||||
$(api.templates.evaluate('tmpl_responsive_table', {
|
||||
tableId: tableId,
|
||||
columns: columns,
|
||||
})).appendTo('head');
|
||||
|
||||
$this.rest.overview(function(data) {
|
||||
var table = gui.table(title, tableId);
|
||||
if (options.container === undefined) {
|
||||
gui.appendToWorkspace('<div class="row"><div class="col-lg-12">' + table.text + '</div></div>');
|
||||
} else {
|
||||
$('#' + options.container).empty();
|
||||
$('#' + options.container).append(table.text);
|
||||
}
|
||||
|
||||
// What execute on refresh button push
|
||||
var onRefresh = options.onRefresh || function(){};
|
||||
|
||||
var refreshFnc = function() {
|
||||
// Refreshes table content
|
||||
var tbl = $('#' + tableId).dataTable();
|
||||
// Clears selection first
|
||||
TableTools.fnGetInstance(tableId).fnSelectNone();
|
||||
if( data.length > 1000 )
|
||||
api.tools.blockUI();
|
||||
|
||||
$this.rest.overview(function(data) {
|
||||
/*$(btn).removeClass('disabled').width('').html(saved);*/
|
||||
setTimeout( function() {
|
||||
tbl.fnClearTable();
|
||||
tbl.fnAddData(data);
|
||||
onRefresh($this);
|
||||
api.tools.unblockUI();
|
||||
}, 0);
|
||||
});
|
||||
return false; // This may be used on button or href, better disable execution of it
|
||||
};
|
||||
|
||||
var btns = [];
|
||||
|
||||
if (options.buttons) {
|
||||
var clickHandlerFor = function(handler, action, newHandler) {
|
||||
var handleFnc = handler || function(val, action, tbl) {gui.doLog('Default handler called for ', action);};
|
||||
return function(btn) {
|
||||
var tbl = $('#' + tableId).dataTable();
|
||||
var val = this.fnGetSelectedData()[0];
|
||||
setTimeout(function() {
|
||||
if( newHandler ) {
|
||||
handleFnc(action, tbl, refreshFnc);
|
||||
} else {
|
||||
handleFnc(val, action, tbl, refreshFnc);
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
};
|
||||
|
||||
// methods for buttons on row select
|
||||
var editSelected = function(btn, obj, node) {
|
||||
var sel = this.fnGetSelectedData();
|
||||
if (sel.length == 1) {
|
||||
$(btn).removeClass('disabled').addClass('btn3d-success');
|
||||
} else {
|
||||
$(btn).removeClass('btn3d-success').addClass('disabled');
|
||||
}
|
||||
};
|
||||
var deleteSelected = function(btn, obj, node) {
|
||||
var sel = this.fnGetSelectedData();
|
||||
if (sel.length > 0) {
|
||||
$(btn).removeClass('disabled').addClass('btn3d-warning');
|
||||
} else {
|
||||
$(btn).removeClass('btn3d-warning').addClass('disabled');
|
||||
}
|
||||
};
|
||||
|
||||
$.each(options.buttons, function(index, value) {
|
||||
var btn;
|
||||
switch (value) {
|
||||
case 'new':
|
||||
if(Object.keys($this.types).length === 0) {
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons['new'].text,
|
||||
"fnClick" : clickHandlerFor(options.onNew, 'new'),
|
||||
"sButtonClass" : gui.config.dataTableButtons['new'].css,
|
||||
};
|
||||
} else {
|
||||
// This table has "types, so we create a dropdown with Types
|
||||
var newButtons = [];
|
||||
// Order buttons by name, much more easy for users... :-)
|
||||
var order = [];
|
||||
$.each($this.types, function(k, v){
|
||||
order.push({
|
||||
type: k,
|
||||
css: v.css,
|
||||
name: v.name,
|
||||
description: v.description,
|
||||
});
|
||||
});
|
||||
$.each(order.sort(function(a,b){return a.name.localeCompare(b.name);}), function(i, val){
|
||||
newButtons.push({
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : '<span class="' + val.css + '"></span> <span data-toggle="tooltip" data-title="' + val.description + '">' + val.name + '</span>',
|
||||
"fnClick" : clickHandlerFor(options.onNew, val.type, true),
|
||||
});
|
||||
});
|
||||
btn = {
|
||||
"sExtends" : "collection",
|
||||
"aButtons": newButtons,
|
||||
"sButtonText" : gui.config.dataTableButtons['new'].text,
|
||||
"sButtonClass" : gui.config.dataTableButtons['new'].css,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons.edit.text,
|
||||
"fnSelect" : editSelected,
|
||||
"fnClick" : clickHandlerFor(options.onEdit, 'edit'),
|
||||
"sButtonClass" : gui.config.dataTableButtons.edit.css,
|
||||
};
|
||||
break;
|
||||
case 'delete':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons['delete'].text,
|
||||
"fnSelect" : deleteSelected,
|
||||
"fnClick" : clickHandlerFor(options.onDelete, 'delete'),
|
||||
"sButtonClass" : gui.config.dataTableButtons['delete'].css,
|
||||
};
|
||||
break;
|
||||
case 'refresh':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons.refresh.text,
|
||||
"fnClick" : refreshFnc,
|
||||
"sButtonClass" : gui.config.dataTableButtons.refresh.css,
|
||||
};
|
||||
break;
|
||||
case 'xls':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons.xls.text,
|
||||
"fnClick" : function(){
|
||||
api.templates.get('spreadsheet', function(tmpl) {
|
||||
var styles = { 'bold': 's21', };
|
||||
var uri = 'data:application/vnd.ms-excel;base64,',
|
||||
base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))); };
|
||||
|
||||
var headings = [], rows = [];
|
||||
$.each(columns, function(index, heading){
|
||||
if( heading.bVisible === false ) {
|
||||
return;
|
||||
}
|
||||
headings.push(api.spreadsheet.cell(heading.sTitle, 'String', styles.bold));
|
||||
});
|
||||
rows.push(api.spreadsheet.row(headings));
|
||||
$.each(data, function(index, row) {
|
||||
var cells = [];
|
||||
$.each(columns, function(index, col){
|
||||
if( col.bVisible === false ) {
|
||||
return;
|
||||
}
|
||||
var type = col.sType == 'numeric' ? 'Number':'String';
|
||||
cells.push(api.spreadsheet.cell(row[col.mData], type));
|
||||
});
|
||||
rows.push(api.spreadsheet.row(cells));
|
||||
});
|
||||
|
||||
var ctx = {
|
||||
creation_date: (new Date()).toISOString(),
|
||||
worksheet: title,
|
||||
columns_count: headings.length,
|
||||
rows_count: rows.length,
|
||||
rows: rows.join('\n')
|
||||
};
|
||||
setTimeout( function() {
|
||||
saveAs(new Blob([api.templates.evaluate(tmpl, ctx)],
|
||||
{type: 'application/vnd.ms-excel'} ), title + '.xls');
|
||||
}, 20);
|
||||
});
|
||||
},
|
||||
"sButtonClass" : gui.config.dataTableButtons.xls.css,
|
||||
};
|
||||
}
|
||||
|
||||
if(btn) {
|
||||
btns.push(btn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initializes oTableTools
|
||||
var oTableTools = {
|
||||
"aButtons" : btns
|
||||
};
|
||||
|
||||
// Type of row selection
|
||||
if (options.rowSelect) {
|
||||
oTableTools.sRowSelect = options.rowSelect;
|
||||
}
|
||||
|
||||
if (options.onRowSelect) {
|
||||
var rowSelectedFnc = options.onRowSelect;
|
||||
oTableTools.fnRowSelected = function() {
|
||||
rowSelectedFnc(this.fnGetSelectedData(), $('#' + tableId).dataTable(), this);
|
||||
};
|
||||
}
|
||||
if (options.onRowDeselect) {
|
||||
var rowDeselectedFnc = options.onRowDeselect;
|
||||
oTableTools.fnRowDeselected = function() {
|
||||
rowDeselectedFnc(this.fnGetSelectedData(), $('#' + tableId).dataTable(), this);
|
||||
};
|
||||
}
|
||||
|
||||
$('#' + tableId).dataTable({
|
||||
"aaData" : data,
|
||||
"aoColumns" : columns,
|
||||
"oLanguage" : gui.config.dataTablesLanguage,
|
||||
"oTableTools" : oTableTools,
|
||||
// First is upper row,
|
||||
// second row is lower
|
||||
// (pagination) row
|
||||
"sDom" : "<'row'<'col-xs-8'T><'col-xs-4'f>r>t<'row'<'col-xs-5'i><'col-xs-7'p>>",
|
||||
|
||||
});
|
||||
// Fix 3dbuttons
|
||||
api.tools.fix3dButtons('#' + tableId + '_wrapper .btn-group-3d');
|
||||
// Fix form
|
||||
$('#' + tableId + '_filter input').addClass('form-control');
|
||||
// Add refresh action to panel
|
||||
$(table.refreshSelector).click(refreshFnc);
|
||||
// Add tooltips to "new" buttons
|
||||
$('.DTTT_dropdown [data-toggle="tooltip"]').tooltip({
|
||||
container:'body',
|
||||
delay: { show: 1000, hide: 100},
|
||||
placement: 'auto right',
|
||||
});
|
||||
|
||||
if (options.scrollToTable === true ) {
|
||||
var tableTop = $('#' + tableId).offset().top;
|
||||
$('html, body').scrollTop(tableTop);
|
||||
}
|
||||
// if table rendered event
|
||||
if( options.onLoad ) {
|
||||
options.onLoad($this);
|
||||
}
|
||||
});
|
||||
});
|
||||
return '#' + tableId;
|
||||
}
|
||||
|
||||
};
|
@ -97,11 +97,24 @@
|
||||
});
|
||||
};
|
||||
|
||||
gui.modal = function(id, title, content) {
|
||||
gui.modal = function(id, title, content, actionButton, closeButton) {
|
||||
return api.templates.evaluate('tmpl_modal', {
|
||||
id: id,
|
||||
title: title,
|
||||
content: content
|
||||
content: content,
|
||||
button1: closeButton,
|
||||
button2: actionButton
|
||||
});
|
||||
};
|
||||
|
||||
gui.launchModal = function(title, content, actionButton, closeButton) {
|
||||
var id = Math.random().toString().split('.')[1]; // Get a random ID for this modal
|
||||
gui.appendToWorkspace(gui.modal(id, title, content, actionButton, closeButton));
|
||||
id = '#' + id; // for jQuery
|
||||
|
||||
$(id).modal()
|
||||
.on('hidden.bs.modal', function () {
|
||||
$(id).remove();
|
||||
});
|
||||
};
|
||||
|
||||
@ -143,10 +156,12 @@
|
||||
if( !$form.valid() )
|
||||
return;
|
||||
if( onSuccess ) {
|
||||
if( onSuccess(id + ' form') === false ) // Some error may have ocurred, do not close dialog
|
||||
onSuccess(id + ' form', function(){$(id).modal('hide');}); // Delegate close to to onSuccess
|
||||
return;
|
||||
} else {
|
||||
$(id).modal('hide');
|
||||
}
|
||||
$(id).modal('hide');
|
||||
|
||||
});
|
||||
|
||||
// Launch modal
|
||||
@ -156,6 +171,16 @@
|
||||
});
|
||||
};
|
||||
|
||||
gui.failRequestMessageFnc = function(jqXHR, textStatus, errorThrown) {
|
||||
api.templates.get('request_failed', function(tmpl) {
|
||||
gui.clearWorkspace();
|
||||
gui.appendToWorkspace(api.templates.evaluate(tmpl, {
|
||||
error: jqXHR.responseText,
|
||||
}));
|
||||
});
|
||||
gui.setLinksEvents();
|
||||
};
|
||||
|
||||
gui.clearWorkspace = function() {
|
||||
$('#content').empty();
|
||||
$('#minimized').empty();
|
||||
@ -233,432 +258,3 @@
|
||||
gui.debug = true;
|
||||
}(window.gui = window.gui || {}, jQuery));
|
||||
|
||||
function BasicGuiElement(name) {
|
||||
"use strict";
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
function GuiElement(restItem, name) {
|
||||
"use strict";
|
||||
this.rest = restItem;
|
||||
this.name = name;
|
||||
this.types = {};
|
||||
this.init();
|
||||
}
|
||||
|
||||
// all gui elements has, at least, name && type
|
||||
// Types must include, at least: type, icon
|
||||
GuiElement.prototype = {
|
||||
init : function() {
|
||||
"use strict";
|
||||
gui.doLog('Initializing ' + this.name);
|
||||
var $this = this;
|
||||
this.rest.types(function(data) {
|
||||
var styles = '';
|
||||
$.each(data, function(index, value) {
|
||||
var className = $this.name + '-' + value.type;
|
||||
$this.types[value.type] = {
|
||||
css : className,
|
||||
name : value.name || '',
|
||||
description : value.description || ''
|
||||
};
|
||||
gui.doLog('Creating style for ' + className);
|
||||
var style = '.' + className + ' { display:inline-block; background: url(data:image/png;base64,' +
|
||||
value.icon + '); ' + 'width: 16px; height: 16px; vertical-align: middle; } ';
|
||||
styles += style;
|
||||
});
|
||||
if (styles !== '') {
|
||||
styles = '<style media="screen">' + styles + '</style>';
|
||||
$(styles).appendTo('head');
|
||||
}
|
||||
});
|
||||
},
|
||||
// Options: dictionary
|
||||
// container: container ID of parent for the table. If undefined, table will be appended to workspace
|
||||
// buttons: array of visible buttons (strings), valid are [ 'new', 'edit', 'refresh', 'delete', 'xls' ],
|
||||
// rowSelect: type of allowed row selection, valid values are 'single' and 'multi'
|
||||
// scrollToTable: if True, will scroll page to show table
|
||||
//
|
||||
// onLoad: Event (function). If defined, will be invoked when table is fully loaded.
|
||||
// Receives 1 parameter, that is the gui element (GuiElement) used to render table
|
||||
// onRowSelect: Event (function). If defined, will be invoked when a row of table is selected
|
||||
// Receives 3 parameters:
|
||||
// 1.- the array of selected items data (objects, as got from api...get)
|
||||
// 2.- the DataTable that raised the event
|
||||
// 3.- the DataTableTools that raised the event
|
||||
// onRowDeselect: Event (function). If defined, will be invoked when a row of table is deselected
|
||||
// Receives 3 parameters:
|
||||
// 1.- the array of selected items data (objects, as got from api...get)
|
||||
// 2.- the DataTable that raised the event
|
||||
// onNew: Event (function). If defined, will be invoked when "new" button is pressed
|
||||
// Receives 4 parameters:
|
||||
// 1.- the selected item data (single object, as got from api...get)
|
||||
// 2.- the event that fired this (new, delete, edit, ..)
|
||||
// 3.- the DataTable that raised the event
|
||||
// onEdit: Event (function). If defined, will be invoked when "edit" button is pressed
|
||||
// Receives 4 parameters:
|
||||
// 1.- the selected item data (single object, as got from api...get)
|
||||
// 2.- the event that fired this (new, delete, edit, ..)
|
||||
// 3.- the DataTable that raised the event
|
||||
// onDelete: Event (function). If defined, will be invoked when "delete" button is pressed
|
||||
// Receives 4 parameters:
|
||||
// 1.- the selected item data (single object, as got from api...get)
|
||||
// 2.- the event that fired this (new, delete, edit, ..)
|
||||
// 4.- the DataTable that raised the event
|
||||
table : function(options) {
|
||||
"use strict";
|
||||
gui.doLog('Types: ', this.types);
|
||||
options = options || {};
|
||||
gui.doLog('Composing table for ' + this.name);
|
||||
var tableId = this.name + '-table';
|
||||
var $this = this; // Store this for child functions
|
||||
|
||||
// Empty cells transform
|
||||
var renderEmptyCell = function(data) {
|
||||
if( data === '' )
|
||||
return '-';
|
||||
return data;
|
||||
};
|
||||
|
||||
// Datetime renderer (with specified format)
|
||||
var renderDate = function(format) {
|
||||
return function(data, type, full) {
|
||||
return api.tools.strftime(format, new Date(data*1000));
|
||||
};
|
||||
};
|
||||
|
||||
// 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;
|
||||
return '<span class="' + css + '"></span> ' + renderEmptyCell(data);
|
||||
} else {
|
||||
return renderEmptyCell(data);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom icon renderer, in fact span with defined class
|
||||
var renderIcon = function(icon) {
|
||||
return function(data, type, full) {
|
||||
if( type == 'display' ) {
|
||||
return '<span class="' + icon + '"></span> ' + renderEmptyCell(data);
|
||||
} else {
|
||||
return renderEmptyCell(data);
|
||||
}
|
||||
};
|
||||
};
|
||||
// Text transformation, dictionary based
|
||||
var renderTextTransform = function(dict) {
|
||||
return function(data, type, full) {
|
||||
return dict[data] || renderEmptyCell('');
|
||||
};
|
||||
};
|
||||
this.rest.tableInfo(function(data) {
|
||||
var title = data.title;
|
||||
var columns = [];
|
||||
$.each(data.fields, function(index, value) {
|
||||
for ( var v in value) {
|
||||
var opts = value[v];
|
||||
var column = {
|
||||
mData : v,
|
||||
};
|
||||
column.sTitle = opts.title;
|
||||
column.mRender = renderEmptyCell;
|
||||
if (opts.width)
|
||||
column.sWidth = opts.width;
|
||||
column.bVisible = opts.visible === undefined ? true : opts.visible;
|
||||
if (opts.sortable !== undefined)
|
||||
column.bSortable = opts.sortable;
|
||||
if (opts.searchable !== undefined)
|
||||
column.bSearchable = opts.searchable;
|
||||
|
||||
if (opts.type && column.bVisible ) {
|
||||
switch(opts.type) {
|
||||
case 'date':
|
||||
column.sType = 'date';
|
||||
column.mRender = renderDate(api.tools.djangoFormat(get_format('SHORT_DATE_FORMAT')));
|
||||
break;
|
||||
case 'datetime':
|
||||
column.sType = 'date';
|
||||
column.mRender = renderDate(api.tools.djangoFormat(get_format('SHORT_DATETIME_FORMAT')));
|
||||
break;
|
||||
case 'time':
|
||||
column.mRender = renderDate(api.tools.djangoFormat(get_format('TIME_FORMAT')));
|
||||
break;
|
||||
case 'iconType':
|
||||
//columnt.sType = 'html'; // html is default, so this is not needed
|
||||
column.mRender = renderTypeIcon;
|
||||
break;
|
||||
case 'icon':
|
||||
if( opts.icon !== undefined ) {
|
||||
column.mRender = renderIcon(opts.icon);
|
||||
}
|
||||
break;
|
||||
case 'dict':
|
||||
if( opts.dict !== undefined ) {
|
||||
column.mRender = renderTextTransform(opts.dict);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
column.sType = opts.type;
|
||||
}
|
||||
}
|
||||
columns.push(column);
|
||||
}
|
||||
});
|
||||
// Responsive style for tables, using tables.css and this code generates the "titles" for vertical display on small sizes
|
||||
$('#style-' + tableId).remove(); // Remove existing style for table before adding new one
|
||||
$(api.templates.evaluate('tmpl_responsive_table', {
|
||||
tableId: tableId,
|
||||
columns: columns,
|
||||
})).appendTo('head');
|
||||
|
||||
$this.rest.overview(function(data) {
|
||||
var table = gui.table(title, tableId);
|
||||
if (options.container === undefined) {
|
||||
gui.appendToWorkspace('<div class="row"><div class="col-lg-12">' + table.text + '</div></div>');
|
||||
} else {
|
||||
$('#' + options.container).empty();
|
||||
$('#' + options.container).append(table.text);
|
||||
}
|
||||
|
||||
// What execute on refresh button push
|
||||
var onRefresh = options.onRefresh || function(){};
|
||||
|
||||
var refreshFnc = function() {
|
||||
// Refreshes table content
|
||||
var tbl = $('#' + tableId).dataTable();
|
||||
// Clears selection first
|
||||
TableTools.fnGetInstance(tableId).fnSelectNone();
|
||||
if( data.length > 1000 )
|
||||
api.tools.blockUI();
|
||||
|
||||
$this.rest.overview(function(data) {
|
||||
/*$(btn).removeClass('disabled').width('').html(saved);*/
|
||||
setTimeout( function() {
|
||||
tbl.fnClearTable();
|
||||
tbl.fnAddData(data);
|
||||
onRefresh($this);
|
||||
api.tools.unblockUI();
|
||||
}, 0);
|
||||
});
|
||||
return false; // This may be used on button or href, better disable execution of it
|
||||
};
|
||||
|
||||
var btns = [];
|
||||
|
||||
if (options.buttons) {
|
||||
var clickHandlerFor = function(handler, action, newHandler) {
|
||||
var handleFnc = handler || function(val, action, tbl) {gui.doLog('Default handler called for ', action);};
|
||||
return function(btn) {
|
||||
var tbl = $('#' + tableId).dataTable();
|
||||
var val = this.fnGetSelectedData()[0];
|
||||
setTimeout(function() {
|
||||
if( newHandler ) {
|
||||
if( handleFnc(action, tbl) === true ) // Reload table?
|
||||
refreshFnc();
|
||||
} else {
|
||||
if( handleFnc(val, action, tbl) === true ) // Reload table?
|
||||
refreshFnc();
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
};
|
||||
|
||||
// methods for buttons on row select
|
||||
var editSelected = function(btn, obj, node) {
|
||||
var sel = this.fnGetSelectedData();
|
||||
if (sel.length == 1) {
|
||||
$(btn).removeClass('disabled').addClass('btn3d-success');
|
||||
} else {
|
||||
$(btn).removeClass('btn3d-success').addClass('disabled');
|
||||
}
|
||||
};
|
||||
var deleteSelected = function(btn, obj, node) {
|
||||
var sel = this.fnGetSelectedData();
|
||||
if (sel.length > 0) {
|
||||
$(btn).removeClass('disabled').addClass('btn3d-warning');
|
||||
} else {
|
||||
$(btn).removeClass('btn3d-warning').addClass('disabled');
|
||||
}
|
||||
};
|
||||
|
||||
$.each(options.buttons, function(index, value) {
|
||||
var btn;
|
||||
switch (value) {
|
||||
case 'new':
|
||||
if(Object.keys($this.types).length === 0) {
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons['new'].text,
|
||||
"fnClick" : clickHandlerFor(options.onNew, 'new'),
|
||||
"sButtonClass" : gui.config.dataTableButtons['new'].css,
|
||||
};
|
||||
} else {
|
||||
// This table has "types, so we create a dropdown with Types
|
||||
var newButtons = [];
|
||||
// Order buttons by name, much more easy for users... :-)
|
||||
var order = [];
|
||||
$.each($this.types, function(k, v){
|
||||
order.push({
|
||||
type: k,
|
||||
css: v.css,
|
||||
name: v.name,
|
||||
description: v.description,
|
||||
});
|
||||
});
|
||||
$.each(order.sort(function(a,b){return a.name.localeCompare(b.name);}), function(i, val){
|
||||
newButtons.push({
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : '<span class="' + val.css + '"></span> <span data-toggle="tooltip" data-title="' + val.description + '">' + val.name + '</span>',
|
||||
"fnClick" : clickHandlerFor(options.onNew, val.type, true),
|
||||
});
|
||||
});
|
||||
btn = {
|
||||
"sExtends" : "collection",
|
||||
"aButtons": newButtons,
|
||||
"sButtonText" : gui.config.dataTableButtons['new'].text,
|
||||
"sButtonClass" : gui.config.dataTableButtons['new'].css,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons.edit.text,
|
||||
"fnSelect" : editSelected,
|
||||
"fnClick" : clickHandlerFor(options.onEdit, 'edit'),
|
||||
"sButtonClass" : gui.config.dataTableButtons.edit.css,
|
||||
};
|
||||
break;
|
||||
case 'delete':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons['delete'].text,
|
||||
"fnSelect" : deleteSelected,
|
||||
"fnClick" : clickHandlerFor(options.onDelete, 'delete'),
|
||||
"sButtonClass" : gui.config.dataTableButtons['delete'].css,
|
||||
};
|
||||
break;
|
||||
case 'refresh':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons.refresh.text,
|
||||
"fnClick" : refreshFnc,
|
||||
"sButtonClass" : gui.config.dataTableButtons.refresh.css,
|
||||
};
|
||||
break;
|
||||
case 'xls':
|
||||
btn = {
|
||||
"sExtends" : "text",
|
||||
"sButtonText" : gui.config.dataTableButtons.xls.text,
|
||||
"fnClick" : function(){
|
||||
api.templates.get('spreadsheet', function(tmpl) {
|
||||
var styles = { 'bold': 's21', };
|
||||
var uri = 'data:application/vnd.ms-excel;base64,',
|
||||
base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))); };
|
||||
|
||||
var headings = [], rows = [];
|
||||
$.each(columns, function(index, heading){
|
||||
if( heading.bVisible === false ) {
|
||||
return;
|
||||
}
|
||||
headings.push(api.spreadsheet.cell(heading.sTitle, 'String', styles.bold));
|
||||
});
|
||||
rows.push(api.spreadsheet.row(headings));
|
||||
$.each(data, function(index, row) {
|
||||
var cells = [];
|
||||
$.each(columns, function(index, col){
|
||||
if( col.bVisible === false ) {
|
||||
return;
|
||||
}
|
||||
var type = col.sType == 'numeric' ? 'Number':'String';
|
||||
cells.push(api.spreadsheet.cell(row[col.mData], type));
|
||||
});
|
||||
rows.push(api.spreadsheet.row(cells));
|
||||
});
|
||||
|
||||
var ctx = {
|
||||
creation_date: (new Date()).toISOString(),
|
||||
worksheet: title,
|
||||
columns_count: headings.length,
|
||||
rows_count: rows.length,
|
||||
rows: rows.join('\n')
|
||||
};
|
||||
setTimeout( function() {
|
||||
saveAs(new Blob([api.templates.evaluate(tmpl, ctx)],
|
||||
{type: 'application/vnd.ms-excel'} ), title + '.xls');
|
||||
}, 20);
|
||||
});
|
||||
},
|
||||
"sButtonClass" : gui.config.dataTableButtons.xls.css,
|
||||
};
|
||||
}
|
||||
|
||||
if(btn) {
|
||||
btns.push(btn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initializes oTableTools
|
||||
var oTableTools = {
|
||||
"aButtons" : btns
|
||||
};
|
||||
|
||||
// Type of row selection
|
||||
if (options.rowSelect) {
|
||||
oTableTools.sRowSelect = options.rowSelect;
|
||||
}
|
||||
|
||||
if (options.onRowSelect) {
|
||||
var rowSelectedFnc = options.onRowSelect;
|
||||
oTableTools.fnRowSelected = function() {
|
||||
rowSelectedFnc(this.fnGetSelectedData(), $('#' + tableId).dataTable(), this);
|
||||
};
|
||||
}
|
||||
if (options.onRowDeselect) {
|
||||
var rowDeselectedFnc = options.onRowDeselect;
|
||||
oTableTools.fnRowDeselected = function() {
|
||||
rowDeselectedFnc(this.fnGetSelectedData(), $('#' + tableId).dataTable(), this);
|
||||
};
|
||||
}
|
||||
|
||||
$('#' + tableId).dataTable({
|
||||
"aaData" : data,
|
||||
"aoColumns" : columns,
|
||||
"oLanguage" : gui.config.dataTablesLanguage,
|
||||
"oTableTools" : oTableTools,
|
||||
// First is upper row,
|
||||
// second row is lower
|
||||
// (pagination) row
|
||||
"sDom" : "<'row'<'col-xs-8'T><'col-xs-4'f>r>t<'row'<'col-xs-5'i><'col-xs-7'p>>",
|
||||
|
||||
});
|
||||
// Fix 3dbuttons
|
||||
api.tools.fix3dButtons('#' + tableId + '_wrapper .btn-group-3d');
|
||||
// Fix form
|
||||
$('#' + tableId + '_filter input').addClass('form-control');
|
||||
// Add refresh action to panel
|
||||
$(table.refreshSelector).click(refreshFnc);
|
||||
// Add tooltips to "new" buttons
|
||||
$('.DTTT_dropdown [data-toggle="tooltip"]').tooltip({
|
||||
container:'body',
|
||||
delay: { show: 1000, hide: 100},
|
||||
placement: 'auto right',
|
||||
});
|
||||
|
||||
if (options.scrollToTable === true ) {
|
||||
var tableTop = $('#' + tableId).offset().top;
|
||||
$('html, body').scrollTop(tableTop);
|
||||
}
|
||||
// if table rendered event
|
||||
if( options.onLoad ) {
|
||||
options.onLoad($this);
|
||||
}
|
||||
});
|
||||
});
|
||||
return '#' + tableId;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -51,7 +51,7 @@
|
||||
<script src="{% get_static_prefix %}js/bootstrap.min.js"></script>
|
||||
<script src="{% get_static_prefix %}js/bootstrap-switch.min.js"></script>
|
||||
<script src="{% get_static_prefix %}js/bootstrap-select.min.js"></script>
|
||||
<script src="{% get_static_prefix %}adm/js/jquery.validate.js"></script>
|
||||
<script src="{% get_static_prefix %}adm/js/jquery.validate.min.js"></script>
|
||||
<script src="{% get_static_prefix %}adm/js/jquery.blockUI.js"></script>
|
||||
<script src="{% get_static_prefix %}adm/js/jquery.dataTables.min.js"></script>
|
||||
|
||||
@ -99,15 +99,17 @@
|
||||
|
||||
<script src="{% get_static_prefix %}adm/js/gui.js"></script>
|
||||
<script src="{% get_static_prefix %}adm/js/gui-fields.js"></script>
|
||||
<script src="{% get_static_prefix %}adm/js/gui-element.js"></script>
|
||||
|
||||
<!-- user interface management -->
|
||||
<script src="{% get_static_prefix %}adm/js/gui-elements.js"></script>
|
||||
<script src="{% get_static_prefix %}adm/js/gui-definition.js"></script>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
// Initialize gui
|
||||
gui.init();
|
||||
|
||||
// set default error function
|
||||
api.defaultFail = gui.failRequestMessageFnc;
|
||||
});
|
||||
</script>
|
||||
{% block js %}{% endblock %}
|
||||
@ -116,6 +118,8 @@
|
||||
<!-- page contents -->
|
||||
{% js_template 'dashboard' %}
|
||||
{% js_template 'authenticators' %}
|
||||
<!-- utility pages -->
|
||||
{% js_template 'request_failed' %}
|
||||
<!-- components -->
|
||||
{% js_template 'table' %}
|
||||
{% js_template 'modal' %}
|
||||
|
@ -19,7 +19,7 @@
|
||||
{% verbatim %}
|
||||
{{/ if }}
|
||||
{{# if button2 }}
|
||||
{{{ button1 }}}
|
||||
{{{ button2 }}}
|
||||
{{ else }}
|
||||
{% endverbatim %}
|
||||
<button type="button" class="btn btn-primary button-accept">{% trans 'Save' %}</button>
|
||||
|
10
server/src/uds/templates/uds/admin/tmpl/request_failed.html
Normal file
10
server/src/uds/templates/uds/admin/tmpl/request_failed.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="jumbotron text-center">
|
||||
<h1> {% trans 'Error on request' %}</h1>
|
||||
<br />
|
||||
{% verbatim %}<p>{{ error }}</p>{% endverbatim %}
|
||||
<h5>{% trans 'There was an error requesting data from server, please, try again' %}</h5>
|
||||
<br />
|
||||
<a href="#" class="btn btn-lg btn-info lnk-dashboard"><i class="fa fa-dashboard"></i> {% trans "Dashboard" %}</a>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user