Advancing on table/model editing

More refactoring of code
This commit is contained in:
Adolfo Gómez 2013-11-29 00:55:04 +00:00
parent c98e964bba
commit 043b28cfb4
13 changed files with 117 additions and 83 deletions

View File

@ -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, RequestError
from handlers import Handler, HandlerError, AccessDenied, NotFound, RequestError, ResponseError
import time
import logging
@ -150,6 +150,8 @@ class Dispatcher(View):
return response
except RequestError as e:
return http.HttpResponseServerError(unicode(e))
except ResponseError as e:
return http.HttpResponseServerError(unicode(e))
except AccessDenied as e:
return http.HttpResponseForbidden(unicode(e))
except NotFound as e:

View File

@ -54,6 +54,9 @@ class AccessDenied(HandlerError):
class RequestError(HandlerError):
pass
class ResponseError(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
@ -86,11 +89,8 @@ class Handler(object):
if not self._session.has_key('REST'):
raise Exception() # No valid session, so auth_token is also invalid
except:
if settings.DEBUG: # Right now all users are valid
self.genAuthToken(-1, 'root', 'es', True, True)
else:
self._authToken = None
self._session = None
self._authToken = None
self._session = None
if self._authToken is None:
raise AccessDenied()

View File

@ -50,6 +50,7 @@ logger = logging.getLogger(__name__)
class Authenticators(ModelHandler):
model = Authenticator
detail = { 'users': Users, 'groups':Groups }
save_fields = ['name', 'comments']
table_title = _('Current authenticators')
table_fields = [
@ -63,7 +64,7 @@ class Authenticators(ModelHandler):
def getGui(self, type_):
try:
return auths.factory().lookup(type_).guiDescription()
return self.addDefaultFields(auths.factory().lookup(type_).guiDescription(), ['name', 'comments'])
except:
raise NotFound('type not found')

View File

@ -33,9 +33,9 @@
from __future__ import unicode_literals
from uds.core.util.Config import GlobalConfig
from uds.models import Authenticator, User
from uds.models import Authenticator
from uds.REST import Handler, HandlerError
from uds.REST import Handler
import logging

View File

@ -37,7 +37,7 @@ from django.utils.translation import ugettext as _
from uds.core.Environment import Environment
from uds.REST.model import DetailHandler
from uds.REST import NotFound
from uds.REST import NotFound, ResponseError
import logging
@ -55,7 +55,8 @@ class Services(DetailHandler):
'name': k.name,
'comments': k.comments,
'type': k.data_type,
'typeName' : _(k.getType().name())
'typeName' : _(k.getType().name()),
'deployed_services_count' : k.deployedServices.count(),
} for k in parent.services.all() ]
else:
with parent.get(pk=item) as k:
@ -64,7 +65,8 @@ class Services(DetailHandler):
'name': k.name,
'comments': k.comments,
'type': k.data_type,
'typeName' : _(k.getType().name())
'typeName' : _(k.getType().name()),
'deployed_services_count' : k.deployedServices.count(),
}
except:
logger.exception('En services')
@ -80,7 +82,8 @@ class Services(DetailHandler):
return [
{ 'name': {'title': _('Service name'), 'visible': True, 'type': 'iconType' } },
{ 'comments': { 'title': _('Comments') } },
{ 'type': {'title': _('Type') } }
{ 'type': {'title': _('Type') } },
{ 'deployed_services_count': {'title': _('Deployed services'), 'type': 'numeric', 'width': '7em'}},
]
def getTypes(self, parent, forType):
@ -103,7 +106,12 @@ class Services(DetailHandler):
return offers # Default is that details do not have types
def getGui(self, parent, forType):
logger.debug('getGui parameters: {0}, {1}'.format(parent, forType))
serviceType = parent.getServiceByType(type)
service = serviceType( Environment.getTempEnv(), parent) # Instantiate it so it has the opportunity to alter gui description based on parent
return self.addDefaultFields( service.guiDescription(service), ['name', 'comments'])
try:
logger.debug('getGui parameters: {0}, {1}'.format(parent, forType))
parentInstance = parent.getInstance()
serviceType = parentInstance.getServiceByType(forType)
service = serviceType( Environment.getTempEnv(), parentInstance) # Instantiate it so it has the opportunity to alter gui description based on parent
return self.addDefaultFields( service.guiDescription(service), ['name', 'comments'])
except Exception as e:
logger.exception('getGui')
raise ResponseError(unicode(e))

View File

@ -48,26 +48,24 @@ logger = logging.getLogger(__name__)
class Users(DetailHandler):
def get(self):
def getItems(self, parent, item):
# Extract authenticator
auth = self._kwargs['parent']
try:
if len(self._args) == 0:
return list(auth.users.all().values('id','name','real_name','comments','state','staff_member','is_admin','last_access','parent'))
if item is None:
return list(parent.users.all().values('id','name','real_name','comments','state','staff_member','is_admin','last_access','parent'))
else:
return auth.get(pk=self._args[0]).values('id','name','real_name','comments','state','staff_member','is_admin','last_access','parent')
return parent.get(pk=item).values('id','name','real_name','comments','state','staff_member','is_admin','last_access','parent')
except:
logger.exception('En users')
return { 'error': 'not found' }
def getTitle(self):
def getTitle(self, parent):
try:
return _('Users of {0}').format(Authenticator.objects.get(pk=self._kwargs['parent_id']).name)
except:
return _('Current users')
def getFields(self):
def getFields(self, parent):
return [
{ 'name': {'title': _('User Id'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-user text-success' } },
{ 'real_name': { 'title': _('Name') } },
@ -77,26 +75,25 @@ class Users(DetailHandler):
]
class Groups(DetailHandler):
def get(self):
def getItems(self, parent, item):
# Extract authenticator
auth = self._kwargs['parent']
try:
if len(self._args) == 0:
return list(auth.groups.all().values('id','name', 'comments','state','is_meta'))
if item is None:
return list(parent.groups.all().values('id','name', 'comments','state','is_meta'))
else:
return auth.get(pk=self._args[0]).values('id','name', 'comments','state','is_meta')
return parent.get(pk=item).values('id','name', 'comments','state','is_meta')
except:
logger.exception('REST groups')
raise HandlerError('exception')
def getTitle(self):
def getTitle(self, parent):
try:
return _('Groups of {0}').format(Authenticator.objects.get(pk=self._kwargs['parent_id']).name)
except:
return _('Current groups')
def getFields(self):
def getFields(self, parent):
return [
{ 'name': {'title': _('User Id'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-group text-success' } },
{ 'comments': { 'title': _('Comments') } },

View File

@ -152,14 +152,13 @@ class DetailHandler(BaseModelHandler):
elif self._args[0] == 'tableinfo':
return self.processTableFields(self.getTitle(parent), self.getFields(parent))
raise RequestError('Invalid request')
#return self.getItems(parent, self._args[0])
# try to get id
return self.getItems(parent, self._args[0])
if nArgs == 2:
if self._args[1] == 'gui':
return self.getGui(parent, self._args[0])
elif self._args[1] == 'types':
if self._args[0] == 'gui':
return self.getGui(parent, self._args[1])
elif self._args[0] == 'types':
return self.getTypes(parent, self._args[1])
return self.fallbackGet()
@ -268,7 +267,7 @@ class ModelHandler(BaseModelHandler):
path = self._path + '/'.join(args[:2])
detail = detailCls(self, path, *args, parent = item)
return getattr(detail, self._operation)()
except:
except AttributeError:
raise NotFound('method not found')
def get(self):
@ -300,7 +299,7 @@ class ModelHandler(BaseModelHandler):
raise NotFound('item not found')
# nArgs > 1
# Request type info
# Request type info or gui, or detail
if self._args[0] == TYPES:
if nArgs != 2:
raise RequestError('invalid request')
@ -348,6 +347,8 @@ class ModelHandler(BaseModelHandler):
item.data_type = self._params['data_type']
item.data = item.getInstance(self._params).serialize()
item.save()
res = self.item_as_dict(item)
self.__fillIntanceFields(item, res)

View File

@ -11,7 +11,6 @@
// Equal comparision (like if helper, but with comparation)
// Use as block as {{#ifequals [element] [element]}}....{{/ifequals}}
Handlebars.registerHelper('ifequals', function(context1, context2, options) {
console.log('Comparing ', context1, ' with ', context2);
if(context1 == context2) {
return options.fn(this);
} else {

View File

@ -350,12 +350,7 @@ BasicModelRest.prototype = {
function DetailModelRestApi(parentApi, parentId, model, options) {
"use strict";
this.options = options;
this.base = new BasicModelRest(undefined, {
getPath: [parentApi.path, parentId, model].join('/'),
typesPath: [parentApi.path, parentId, model, 'types'].join('/'), // Proably this will return nothing
guiPath: [parentApi.path, parentId, model].join('/'), // Proably this will return nothing
tableInfoPath: [parentApi.path, parentId, model, 'tableinfo'].join('/'),
});
this.base = new BasicModelRest([parentApi.path, parentId, model].join('/'));
}
DetailModelRestApi.prototype = {
@ -378,7 +373,7 @@ DetailModelRestApi.prototype = {
},
item: function(itemId, success_fnc, fail_fnc) {
"use strict";
return this.base.item(success_fnc, fail_fnc);
return this.base.item(itemId, success_fnc, fail_fnc);
},
types: function(success_fnc, fail_fnc) {
"use strict";

View File

@ -40,23 +40,38 @@ gui.providers.link = function(event) {
var tableId = gui.providers.table({
container : 'providers-placeholder',
rowSelect : 'single',
onCheck : function(check, items) { // Check if item can be deleted
if( check == 'delete' ) {
for( var i in items ) {
if( items[i].services_count > 0)
return false;
}
return true;
}
return true;
},
onRowSelect : function(selected) {
api.tools.blockUI();
gui.doLog(selected[0]);
var id = selected[0].id;
// Options for detail, to initialize types correctly
var detail_options = {
/* types: function(success_fnc, fail_fnc) {
success_fnc(selected[0].offers);
}*/
};
// Giving the name compossed with type, will ensure that only styles will be reattached once
var services = new GuiElement(api.providers.detail(id, 'services', detail_options), 'services-'+selected[0].type);
var services = new GuiElement(api.providers.detail(id, 'services'), 'services-'+selected[0].type);
services.table({
container : 'services-placeholder',
rowSelect : 'single',
onCheck: function(check, items) {
if( check == 'delete' ) {
for( var i in items ) {
if( items[i].deployed_services_count > 0)
return false;
}
return true;
}
return true;
},
buttons : [ 'new', 'edit', 'delete', 'xls' ],
onEdit : services.typedEdit(gettext('Edit service'), gettext('Error processing service')),
scrollToTable : false,
onLoad: function(k) {
api.tools.unblockUI();
@ -65,23 +80,7 @@ gui.providers.link = function(event) {
return false;
},
buttons : [ 'new', 'edit', 'delete', 'xls' ],
onEdit: function(value, event, table, refreshFnc) {
gui.providers.rest.gui(value.type, function(itemGui){
gui.providers.rest.item(value.id, function(item) {
gui.forms.launchModal(gettext('Edit Service Provider')+' '+value.name, itemGui, item, function(form_selector, closeFnc) {
var fields = gui.forms.read(form_selector);
fields.data_type = value.type;
fields.nets_positive = false;
gui.providers.rest.save(fields, function(data) { // Success on put
closeFnc();
refreshFnc();
}, gui.failRequestModalFnc(gettext('Error creating Service Provider')) // Fail on put, show modal message
);
return false;
});
});
});
},
onEdit: gui.providers.typedEdit(gettext('Edit provider'), gettext('Error processing provider')),
});
});
@ -136,12 +135,8 @@ gui.authenticators.link = function(event) {
onRefresh : function() {
$('#users-placeholder').empty(); // Remove detail on parent refresh
},
onEdit: function(value, event, table) {
gui.authenticators.rest.gui(value.type, function(data){
var form = gui.fields(data);
gui.launchModalForm(gettext('Edit authenticator')+' '+value.name, form);
});
},
onEdit: gui.authenticators.typedEdit(gettext('Edit authenticator'), gettext('Error processing authenticator')),
});
});

View File

@ -61,6 +61,11 @@ GuiElement.prototype = {
// Receives 3 parameters:
// 1.- the array of selected items data (objects, as got from api...get)
// 2.- the DataTable that raised the event
// onCheck: Event (function),
// It determines the state of buttons on selection: if returns "true", the indicated button will be enabled, and disabled if returns "false"
// Receives 2 parameters:
// 1.- the event fired, that can be "edit" or "delete"
// 2.- the selected items data (array of selected elements, as got from api...get. In case of edit, array length will be 1)
// 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)
@ -240,10 +245,14 @@ GuiElement.prototype = {
};
};
var onCheck = options.onCheck || function() { return true }; // Default oncheck always returns true
// methods for buttons on row select
var editSelected = function(btn, obj, node) {
var sel = this.fnGetSelectedData();
if (sel.length == 1) {
var enable = sel.length == 1 ? onCheck("edit", sel) : false;
if ( enable) {
$(btn).removeClass('disabled').addClass('btn3d-success');
} else {
$(btn).removeClass('btn3d-success').addClass('disabled');
@ -251,7 +260,9 @@ GuiElement.prototype = {
};
var deleteSelected = function(btn, obj, node) {
var sel = this.fnGetSelectedData();
if (sel.length > 0) {
var enable = sel.length == 1 ? onCheck("delete", sel) : false;
if (enable) {
$(btn).removeClass('disabled').addClass('btn3d-warning');
} else {
$(btn).removeClass('btn3d-warning').addClass('disabled');
@ -434,6 +445,29 @@ GuiElement.prototype = {
}); // End Overview data
}); // End Tableinfo data
return '#' + tableId;
}
},
// "Generic" edit method to set onEdit table
typedEdit: function(modalTitle, modalErrorMsg, guiProcessor, fieldsProcessor) {
"use strict";
var self = this;
return function(value, event, table, refreshFnc) {
self.rest.gui(value.type, function(guiDefinition) {
var tabs = guiProcessor ? guiProcessor(guiDefinition) : guiDefinition;
self.rest.item(value.id, function(item) {
gui.forms.launchModal(modalTitle+' <b>'+value.name+'</b>', tabs, item, function(form_selector, closeFnc) {
var fields = gui.forms.read(form_selector);
fields.data_type = value.type;
fields = fieldsProcessor ? fieldsProcessor(fields) : fields; // Preprocess fields (probably generate tabs...)
self.rest.save(fields, function(data) { // Success on put
closeFnc();
refreshFnc();
}, gui.failRequestModalFnc(modalErrorMsg)); // Fail on put, show modal message
return false;
});
});
}, gui.failRequestModalFnc(modalErrorMsg));
};
},
};

View File

@ -203,6 +203,8 @@
max: $.validator.format(gettext("Please enter a value less than or equal to {0}.")),
min: $.validator.format(gettext("Please enter a value greater than or equal to {0}."))
});
// Set blockui params
$.blockUI.defaults.baseZ = 2000;
gui.setLinksEvents();
gui.dashboard.link();

View File

@ -5,7 +5,7 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{{ title }}</h4>
<h4 class="modal-title">{{{ title }}}</h4>
</div>
<div class="modal-body">
{{{ content }}}