1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-03 01:17:56 +03:00

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

View File

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

View File

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

View File

@ -33,9 +33,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from uds.core.util.Config import GlobalConfig 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 import logging

View File

@ -37,7 +37,7 @@ from django.utils.translation import ugettext as _
from uds.core.Environment import Environment from uds.core.Environment import Environment
from uds.REST.model import DetailHandler from uds.REST.model import DetailHandler
from uds.REST import NotFound from uds.REST import NotFound, ResponseError
import logging import logging
@ -55,7 +55,8 @@ class Services(DetailHandler):
'name': k.name, 'name': k.name,
'comments': k.comments, 'comments': k.comments,
'type': k.data_type, 'type': k.data_type,
'typeName' : _(k.getType().name()) 'typeName' : _(k.getType().name()),
'deployed_services_count' : k.deployedServices.count(),
} for k in parent.services.all() ] } for k in parent.services.all() ]
else: else:
with parent.get(pk=item) as k: with parent.get(pk=item) as k:
@ -64,7 +65,8 @@ class Services(DetailHandler):
'name': k.name, 'name': k.name,
'comments': k.comments, 'comments': k.comments,
'type': k.data_type, 'type': k.data_type,
'typeName' : _(k.getType().name()) 'typeName' : _(k.getType().name()),
'deployed_services_count' : k.deployedServices.count(),
} }
except: except:
logger.exception('En services') logger.exception('En services')
@ -80,7 +82,8 @@ class Services(DetailHandler):
return [ return [
{ 'name': {'title': _('Service name'), 'visible': True, 'type': 'iconType' } }, { 'name': {'title': _('Service name'), 'visible': True, 'type': 'iconType' } },
{ 'comments': { 'title': _('Comments') } }, { 'comments': { 'title': _('Comments') } },
{ 'type': {'title': _('Type') } } { 'type': {'title': _('Type') } },
{ 'deployed_services_count': {'title': _('Deployed services'), 'type': 'numeric', 'width': '7em'}},
] ]
def getTypes(self, parent, forType): def getTypes(self, parent, forType):
@ -103,7 +106,12 @@ class Services(DetailHandler):
return offers # Default is that details do not have types return offers # Default is that details do not have types
def getGui(self, parent, forType): def getGui(self, parent, forType):
logger.debug('getGui parameters: {0}, {1}'.format(parent, forType)) try:
serviceType = parent.getServiceByType(type) logger.debug('getGui parameters: {0}, {1}'.format(parent, forType))
service = serviceType( Environment.getTempEnv(), parent) # Instantiate it so it has the opportunity to alter gui description based on parent parentInstance = parent.getInstance()
return self.addDefaultFields( service.guiDescription(service), ['name', 'comments']) 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): class Users(DetailHandler):
def get(self): def getItems(self, parent, item):
# Extract authenticator # Extract authenticator
auth = self._kwargs['parent']
try: try:
if len(self._args) == 0: if item is None:
return list(auth.users.all().values('id','name','real_name','comments','state','staff_member','is_admin','last_access','parent')) return list(parent.users.all().values('id','name','real_name','comments','state','staff_member','is_admin','last_access','parent'))
else: 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: except:
logger.exception('En users') logger.exception('En users')
return { 'error': 'not found' } return { 'error': 'not found' }
def getTitle(self): def getTitle(self, parent):
try: try:
return _('Users of {0}').format(Authenticator.objects.get(pk=self._kwargs['parent_id']).name) return _('Users of {0}').format(Authenticator.objects.get(pk=self._kwargs['parent_id']).name)
except: except:
return _('Current users') return _('Current users')
def getFields(self): def getFields(self, parent):
return [ return [
{ 'name': {'title': _('User Id'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-user text-success' } }, { 'name': {'title': _('User Id'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-user text-success' } },
{ 'real_name': { 'title': _('Name') } }, { 'real_name': { 'title': _('Name') } },
@ -77,26 +75,25 @@ class Users(DetailHandler):
] ]
class Groups(DetailHandler): class Groups(DetailHandler):
def get(self):
def getItems(self, parent, item):
# Extract authenticator # Extract authenticator
auth = self._kwargs['parent']
try: try:
if len(self._args) == 0: if item is None:
return list(auth.groups.all().values('id','name', 'comments','state','is_meta')) return list(parent.groups.all().values('id','name', 'comments','state','is_meta'))
else: 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: except:
logger.exception('REST groups') logger.exception('REST groups')
raise HandlerError('exception') raise HandlerError('exception')
def getTitle(self): def getTitle(self, parent):
try: try:
return _('Groups of {0}').format(Authenticator.objects.get(pk=self._kwargs['parent_id']).name) return _('Groups of {0}').format(Authenticator.objects.get(pk=self._kwargs['parent_id']).name)
except: except:
return _('Current groups') return _('Current groups')
def getFields(self): def getFields(self, parent):
return [ return [
{ 'name': {'title': _('User Id'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-group text-success' } }, { 'name': {'title': _('User Id'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-group text-success' } },
{ 'comments': { 'title': _('Comments') } }, { 'comments': { 'title': _('Comments') } },

View File

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

View File

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

View File

@ -350,12 +350,7 @@ BasicModelRest.prototype = {
function DetailModelRestApi(parentApi, parentId, model, options) { function DetailModelRestApi(parentApi, parentId, model, options) {
"use strict"; "use strict";
this.options = options; this.options = options;
this.base = new BasicModelRest(undefined, { this.base = new BasicModelRest([parentApi.path, parentId, model].join('/'));
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('/'),
});
} }
DetailModelRestApi.prototype = { DetailModelRestApi.prototype = {
@ -378,7 +373,7 @@ DetailModelRestApi.prototype = {
}, },
item: function(itemId, success_fnc, fail_fnc) { item: function(itemId, success_fnc, fail_fnc) {
"use strict"; "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) { types: function(success_fnc, fail_fnc) {
"use strict"; "use strict";

View File

@ -40,23 +40,38 @@ gui.providers.link = function(event) {
var tableId = gui.providers.table({ var tableId = gui.providers.table({
container : 'providers-placeholder', container : 'providers-placeholder',
rowSelect : 'single', 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) { onRowSelect : function(selected) {
api.tools.blockUI(); api.tools.blockUI();
gui.doLog(selected[0]); gui.doLog(selected[0]);
var id = selected[0].id; 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 // 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({ services.table({
container : 'services-placeholder', container : 'services-placeholder',
rowSelect : 'single', 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' ], buttons : [ 'new', 'edit', 'delete', 'xls' ],
onEdit : services.typedEdit(gettext('Edit service'), gettext('Error processing service')),
scrollToTable : false, scrollToTable : false,
onLoad: function(k) { onLoad: function(k) {
api.tools.unblockUI(); api.tools.unblockUI();
@ -65,23 +80,7 @@ gui.providers.link = function(event) {
return false; return false;
}, },
buttons : [ 'new', 'edit', 'delete', 'xls' ], buttons : [ 'new', 'edit', 'delete', 'xls' ],
onEdit: function(value, event, table, refreshFnc) { onEdit: gui.providers.typedEdit(gettext('Edit provider'), gettext('Error processing provider')),
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;
});
});
});
},
}); });
}); });
@ -136,12 +135,8 @@ gui.authenticators.link = function(event) {
onRefresh : function() { onRefresh : function() {
$('#users-placeholder').empty(); // Remove detail on parent refresh $('#users-placeholder').empty(); // Remove detail on parent refresh
}, },
onEdit: function(value, event, table) { onEdit: gui.authenticators.typedEdit(gettext('Edit authenticator'), gettext('Error processing authenticator')),
gui.authenticators.rest.gui(value.type, function(data){
var form = gui.fields(data);
gui.launchModalForm(gettext('Edit authenticator')+' '+value.name, form);
});
},
}); });
}); });

View File

@ -61,6 +61,11 @@ GuiElement.prototype = {
// Receives 3 parameters: // Receives 3 parameters:
// 1.- the array of selected items data (objects, as got from api...get) // 1.- the array of selected items data (objects, as got from api...get)
// 2.- the DataTable that raised the event // 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 // onNew: Event (function). If defined, will be invoked when "new" button is pressed
// Receives 4 parameters: // Receives 4 parameters:
// 1.- the selected item data (single object, as got from api...get) // 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 // methods for buttons on row select
var editSelected = function(btn, obj, node) { var editSelected = function(btn, obj, node) {
var sel = this.fnGetSelectedData(); 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'); $(btn).removeClass('disabled').addClass('btn3d-success');
} else { } else {
$(btn).removeClass('btn3d-success').addClass('disabled'); $(btn).removeClass('btn3d-success').addClass('disabled');
@ -251,7 +260,9 @@ GuiElement.prototype = {
}; };
var deleteSelected = function(btn, obj, node) { var deleteSelected = function(btn, obj, node) {
var sel = this.fnGetSelectedData(); 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'); $(btn).removeClass('disabled').addClass('btn3d-warning');
} else { } else {
$(btn).removeClass('btn3d-warning').addClass('disabled'); $(btn).removeClass('btn3d-warning').addClass('disabled');
@ -434,6 +445,29 @@ GuiElement.prototype = {
}); // End Overview data }); // End Overview data
}); // End Tableinfo data }); // End Tableinfo data
return '#' + tableId; 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}.")), 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}.")) min: $.validator.format(gettext("Please enter a value greater than or equal to {0}."))
}); });
// Set blockui params
$.blockUI.defaults.baseZ = 2000;
gui.setLinksEvents(); gui.setLinksEvents();
gui.dashboard.link(); gui.dashboard.link();

View File

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