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.decorators import method_decorator
|
||||||
from django.utils.translation import ugettext as _, activate
|
from django.utils.translation import ugettext as _, activate
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from handlers import Handler, HandlerError, AccessDenied, NotFound
|
from handlers import Handler, HandlerError, AccessDenied, NotFound, RequestError
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
@ -148,12 +148,14 @@ class Dispatcher(View):
|
|||||||
for k, v in handler.headers().iteritems():
|
for k, v in handler.headers().iteritems():
|
||||||
response[k] = v
|
response[k] = v
|
||||||
return response
|
return response
|
||||||
except HandlerError as e:
|
except RequestError as e:
|
||||||
return http.HttpResponseBadRequest(unicode(e))
|
return http.HttpResponseServerError(unicode(e))
|
||||||
except AccessDenied as e:
|
except AccessDenied as e:
|
||||||
return http.HttpResponseForbidden(unicode(e))
|
return http.HttpResponseForbidden(unicode(e))
|
||||||
except NotFound as e:
|
except NotFound as e:
|
||||||
return http.Http404(unicode(e))
|
return http.Http404(unicode(e))
|
||||||
|
except HandlerError as e:
|
||||||
|
return http.HttpResponseBadRequest(unicode(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception('Error processing request')
|
logger.exception('Error processing request')
|
||||||
return http.HttpResponseServerError(unicode(e))
|
return http.HttpResponseServerError(unicode(e))
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
'''
|
'''
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django.contrib.sessions.backends.db import SessionStore
|
from django.contrib.sessions.backends.db import SessionStore
|
||||||
from django.utils.translation import activate
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from uds.core.util.Config import GlobalConfig
|
from uds.core.util.Config import GlobalConfig
|
||||||
@ -52,6 +51,9 @@ class NotFound(HandlerError):
|
|||||||
class AccessDenied(HandlerError):
|
class AccessDenied(HandlerError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class RequestError(HandlerError):
|
||||||
|
pass
|
||||||
|
|
||||||
class Handler(object):
|
class Handler(object):
|
||||||
raw = False # If true, Handler will return directly an HttpResponse 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
|
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 __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.models import Transport
|
||||||
from uds.core.transports import factory
|
from uds.core.transports import factory
|
||||||
|
|
||||||
@ -47,6 +47,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Transports(ModelHandlerMixin, Handler):
|
class Transports(ModelHandlerMixin, Handler):
|
||||||
model = Transport
|
model = Transport
|
||||||
|
save_fields = ['name', 'comments', 'priority', 'nets_positive']
|
||||||
|
|
||||||
def item_as_dict(self, item):
|
def item_as_dict(self, item):
|
||||||
type_ = item.getType()
|
type_ = item.getType()
|
||||||
@ -58,16 +59,27 @@ class Transports(ModelHandlerMixin, Handler):
|
|||||||
'deployed_count': item.deployedServices.count(),
|
'deployed_count': item.deployedServices.count(),
|
||||||
'type': type_.type(),
|
'type': type_.type(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Types(ModelTypeHandlerMixin, Handler):
|
class Types(ModelTypeHandlerMixin, Handler):
|
||||||
path = 'transports'
|
path = 'transports'
|
||||||
|
has_comments = True
|
||||||
|
|
||||||
def enum_types(self):
|
def enum_types(self):
|
||||||
return factory().providers().values()
|
return factory().providers().values()
|
||||||
|
|
||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
try:
|
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:
|
except:
|
||||||
raise NotFound('type not found')
|
raise NotFound('type not found')
|
||||||
|
|
||||||
@ -78,5 +90,6 @@ class TableInfo(ModelTableHandlerMixin, Handler):
|
|||||||
fields = [
|
fields = [
|
||||||
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
||||||
{ 'comments': {'title': _('Comments')}},
|
{ 'comments': {'title': _('Comments')}},
|
||||||
|
{ 'priority': {'title': _('Priority'), 'type': 'numeric', 'width': '6em' }},
|
||||||
{ 'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
{ 'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
||||||
]
|
]
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
'''
|
'''
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from handlers import NotFound
|
from handlers import NotFound, RequestError
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -65,6 +65,8 @@ class ModelHandlerMixin(object):
|
|||||||
needs_staff = True
|
needs_staff = True
|
||||||
detail = None # Dictionary containing detail routing
|
detail = None # Dictionary containing detail routing
|
||||||
model = None
|
model = None
|
||||||
|
save_fields = []
|
||||||
|
|
||||||
|
|
||||||
def item_as_dict(self, item):
|
def item_as_dict(self, item):
|
||||||
pass
|
pass
|
||||||
@ -117,6 +119,37 @@ class ModelHandlerMixin(object):
|
|||||||
return res
|
return res
|
||||||
except:
|
except:
|
||||||
raise NotFound('item not found')
|
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):
|
class ModelTypeHandlerMixin(object):
|
||||||
'''
|
'''
|
||||||
@ -125,6 +158,7 @@ class ModelTypeHandlerMixin(object):
|
|||||||
'''
|
'''
|
||||||
authenticated = True
|
authenticated = True
|
||||||
needs_staff = True
|
needs_staff = True
|
||||||
|
has_comments = False
|
||||||
|
|
||||||
def enum_types(self):
|
def enum_types(self):
|
||||||
pass
|
pass
|
||||||
@ -140,6 +174,46 @@ class ModelTypeHandlerMixin(object):
|
|||||||
for type_ in self.enum_types():
|
for type_ in self.enum_types():
|
||||||
yield self.type_as_dict(type_)
|
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):
|
def get(self):
|
||||||
logger.debug(self._args)
|
logger.debug(self._args)
|
||||||
nArgs = len(self._args)
|
nArgs = len(self._args)
|
||||||
@ -162,42 +236,7 @@ class ModelTypeHandlerMixin(object):
|
|||||||
if self._args[1] == 'gui':
|
if self._args[1] == 'gui':
|
||||||
gui = self.getGui(self._args[0])
|
gui = self.getGui(self._args[0])
|
||||||
# Add name default description, at top of form
|
# 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))
|
logger.debug("GUI: {0}".format(gui))
|
||||||
return sorted(gui, key=lambda f: f['gui']['order']);
|
return sorted(gui, key=lambda f: f['gui']['order']);
|
||||||
|
|
||||||
|
@ -125,7 +125,9 @@ class gui(object):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the string is "true" (case insensitive), False else.
|
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 True
|
||||||
return False
|
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) {
|
api.getJson = function(path, options) {
|
||||||
options = 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);
|
var url = api.url_for(path);
|
||||||
api.doLog('Ajax GET Json for "' + url + '"');
|
api.doLog('Ajax GET Json for "' + url + '"');
|
||||||
@ -51,10 +57,41 @@
|
|||||||
type : "GET",
|
type : "GET",
|
||||||
dataType : "json",
|
dataType : "json",
|
||||||
success : function(data) {
|
success : function(data) {
|
||||||
api.doLog('Success on "' + url + '".');
|
api.doLog('Success on GET "' + url + '".');
|
||||||
api.doLog('Received ' + JSON.stringify(data));
|
api.doLog('Received ', data);
|
||||||
success_fnc(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) {
|
beforeSend : function(request) {
|
||||||
request.setRequestHeader(api.config.auth_header, api.config.token);
|
request.setRequestHeader(api.config.auth_header, api.config.token);
|
||||||
},
|
},
|
||||||
@ -62,7 +99,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Public attributes
|
// Public attributes
|
||||||
api.debug = false;
|
api.debug = true;
|
||||||
}(window.api = window.api || {}, jQuery));
|
}(window.api = window.api || {}, jQuery));
|
||||||
|
|
||||||
|
|
||||||
@ -104,6 +141,7 @@ function BasicModelRest(path, options) {
|
|||||||
// Requests paths
|
// Requests paths
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.getPath = options.getPath || path;
|
this.getPath = options.getPath || path;
|
||||||
|
this.putPath = options.putPath || path;
|
||||||
this.typesPath = options.typesPath || (path + '/types');
|
this.typesPath = options.typesPath || (path + '/types');
|
||||||
this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo');
|
this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo');
|
||||||
this.cache = api.cache('bmr'+path);
|
this.cache = api.cache('bmr'+path);
|
||||||
@ -117,7 +155,8 @@ BasicModelRest.prototype = {
|
|||||||
_requestPath: function(path, options) {
|
_requestPath: function(path, options) {
|
||||||
"use strict";
|
"use strict";
|
||||||
options = options || {};
|
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;
|
var cacheKey = options.cacheKey || path;
|
||||||
|
|
||||||
if( path == '.' ) {
|
if( path == '.' ) {
|
||||||
@ -136,10 +175,11 @@ BasicModelRest.prototype = {
|
|||||||
}
|
}
|
||||||
success_fnc(data);
|
success_fnc(data);
|
||||||
},
|
},
|
||||||
|
fail: fail_fnc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get : function(success_fnc, options) {
|
get: function(options) {
|
||||||
"use strict";
|
"use strict";
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
@ -148,61 +188,103 @@ BasicModelRest.prototype = {
|
|||||||
path += '/' + options.id;
|
path += '/' + options.id;
|
||||||
return this._requestPath(path, {
|
return this._requestPath(path, {
|
||||||
cacheKey: '.', // Right now, do not cache any "get" method
|
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";
|
"use strict";
|
||||||
options = options || {};
|
return this.get({
|
||||||
return this.get(success_fnc, {
|
|
||||||
id: '',
|
id: '',
|
||||||
|
success: success_fnc,
|
||||||
|
fail: fail_fnc
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
overview: function(success_fnc, options) {
|
overview: function(success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
options = options || {};
|
return this.get({
|
||||||
return this.get(success_fnc, {
|
|
||||||
id: 'overview',
|
id: 'overview',
|
||||||
|
success: success_fnc,
|
||||||
|
fail: fail_fnc
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
item: function(itemId, success_fnc, options) {
|
item: function(itemId, success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
options = options || {};
|
return this.get({
|
||||||
return this.get(success_fnc, {
|
|
||||||
id: itemId,
|
id: itemId,
|
||||||
|
success: success_fnc,
|
||||||
|
fail: fail_fnc
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
types : function(success_fnc, options) {
|
|
||||||
|
// -------------
|
||||||
|
// Put methods
|
||||||
|
// -------------
|
||||||
|
|
||||||
|
put: function(data, options) {
|
||||||
"use strict";
|
"use strict";
|
||||||
options = options || {};
|
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, {
|
return this._requestPath(this.typesPath, {
|
||||||
cacheKey: 'type',
|
cacheKey: 'type',
|
||||||
success: success_fnc,
|
success: success_fnc,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
gui: function(typeName, success_fnc, options) {
|
gui: function(typeName, success_fnc, fail_fnc) {
|
||||||
// GUI returns a dict, that contains:
|
// GUI returns a dict, that contains:
|
||||||
// name: Name of the field
|
// name: Name of the field
|
||||||
// value: value of the field (selected element in choice, text for inputs, etc....)
|
// value: value of the field (selected element in choice, text for inputs, etc....)
|
||||||
// gui: Description of the field (type, value or values, defvalue, ....
|
// gui: Description of the field (type, value or values, defvalue, ....
|
||||||
"use strict";
|
"use strict";
|
||||||
options = options || {};
|
|
||||||
var path = [this.typesPath, typeName, 'gui'].join('/');
|
var path = [this.typesPath, typeName, 'gui'].join('/');
|
||||||
return this._requestPath(path, {
|
return this._requestPath(path, {
|
||||||
cacheKey: typeName + '-gui',
|
cacheKey: typeName + '-gui',
|
||||||
success: success_fnc,
|
success: success_fnc,
|
||||||
|
fail: fail_fnc,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
tableInfo : function(success_fnc, options) {
|
tableInfo : function(success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
options = options || {};
|
|
||||||
success_fnc = success_fnc || function(){api.doLog('success not provided for tableInfo');};
|
success_fnc = success_fnc || function(){api.doLog('success not provided for tableInfo');};
|
||||||
|
|
||||||
var path = this.tableInfoPath;
|
var path = this.tableInfoPath;
|
||||||
this._requestPath(path, {
|
this._requestPath(path, {
|
||||||
success: success_fnc,
|
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) {
|
gui.connectivity.transports.rest.gui(type, function(itemGui) {
|
||||||
var form = gui.fields(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);
|
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', {
|
return api.templates.evaluate('tmpl_modal', {
|
||||||
id: id,
|
id: id,
|
||||||
title: title,
|
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() )
|
if( !$form.valid() )
|
||||||
return;
|
return;
|
||||||
if( onSuccess ) {
|
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;
|
return;
|
||||||
|
} else {
|
||||||
|
$(id).modal('hide');
|
||||||
}
|
}
|
||||||
$(id).modal('hide');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Launch modal
|
// 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() {
|
gui.clearWorkspace = function() {
|
||||||
$('#content').empty();
|
$('#content').empty();
|
||||||
$('#minimized').empty();
|
$('#minimized').empty();
|
||||||
@ -233,432 +258,3 @@
|
|||||||
gui.debug = true;
|
gui.debug = true;
|
||||||
}(window.gui = window.gui || {}, jQuery));
|
}(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.min.js"></script>
|
||||||
<script src="{% get_static_prefix %}js/bootstrap-switch.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 %}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.blockUI.js"></script>
|
||||||
<script src="{% get_static_prefix %}adm/js/jquery.dataTables.min.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.js"></script>
|
||||||
<script src="{% get_static_prefix %}adm/js/gui-fields.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 -->
|
<!-- 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>
|
<script>
|
||||||
$(function() {
|
$(function() {
|
||||||
// Initialize gui
|
// Initialize gui
|
||||||
gui.init();
|
gui.init();
|
||||||
|
// set default error function
|
||||||
|
api.defaultFail = gui.failRequestMessageFnc;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% block js %}{% endblock %}
|
{% block js %}{% endblock %}
|
||||||
@ -116,6 +118,8 @@
|
|||||||
<!-- page contents -->
|
<!-- page contents -->
|
||||||
{% js_template 'dashboard' %}
|
{% js_template 'dashboard' %}
|
||||||
{% js_template 'authenticators' %}
|
{% js_template 'authenticators' %}
|
||||||
|
<!-- utility pages -->
|
||||||
|
{% js_template 'request_failed' %}
|
||||||
<!-- components -->
|
<!-- components -->
|
||||||
{% js_template 'table' %}
|
{% js_template 'table' %}
|
||||||
{% js_template 'modal' %}
|
{% js_template 'modal' %}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
{% verbatim %}
|
{% verbatim %}
|
||||||
{{/ if }}
|
{{/ if }}
|
||||||
{{# if button2 }}
|
{{# if button2 }}
|
||||||
{{{ button1 }}}
|
{{{ button2 }}}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
{% endverbatim %}
|
{% endverbatim %}
|
||||||
<button type="button" class="btn btn-primary button-accept">{% trans 'Save' %}</button>
|
<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