diff --git a/server/src/uds/REST/__init__.py b/server/src/uds/REST/__init__.py index c0f54e259..8f9636d74 100644 --- a/server/src/uds/REST/__init__.py +++ b/server/src/uds/REST/__init__.py @@ -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: diff --git a/server/src/uds/REST/handlers.py b/server/src/uds/REST/handlers.py index 26e14cb22..ac0957633 100644 --- a/server/src/uds/REST/handlers.py +++ b/server/src/uds/REST/handlers.py @@ -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() diff --git a/server/src/uds/REST/methods/authenticators.py b/server/src/uds/REST/methods/authenticators.py index 16981fff7..459515005 100644 --- a/server/src/uds/REST/methods/authenticators.py +++ b/server/src/uds/REST/methods/authenticators.py @@ -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') diff --git a/server/src/uds/REST/methods/login_logout.py b/server/src/uds/REST/methods/login_logout.py index 9376f226b..4a1d4142c 100644 --- a/server/src/uds/REST/methods/login_logout.py +++ b/server/src/uds/REST/methods/login_logout.py @@ -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 diff --git a/server/src/uds/REST/methods/services.py b/server/src/uds/REST/methods/services.py index 462540b5c..20a6dbb66 100644 --- a/server/src/uds/REST/methods/services.py +++ b/server/src/uds/REST/methods/services.py @@ -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)) diff --git a/server/src/uds/REST/methods/users_groups.py b/server/src/uds/REST/methods/users_groups.py index 2d73a2f77..0b073e3e6 100644 --- a/server/src/uds/REST/methods/users_groups.py +++ b/server/src/uds/REST/methods/users_groups.py @@ -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') } }, diff --git a/server/src/uds/REST/model.py b/server/src/uds/REST/model.py index 4443e3c47..dd618c52b 100644 --- a/server/src/uds/REST/model.py +++ b/server/src/uds/REST/model.py @@ -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) diff --git a/server/src/uds/static/adm/js/api-templates.js b/server/src/uds/static/adm/js/api-templates.js index 4b9ce31d3..1e7a1a151 100644 --- a/server/src/uds/static/adm/js/api-templates.js +++ b/server/src/uds/static/adm/js/api-templates.js @@ -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 { diff --git a/server/src/uds/static/adm/js/api.js b/server/src/uds/static/adm/js/api.js index b887c4146..151758cd1 100644 --- a/server/src/uds/static/adm/js/api.js +++ b/server/src/uds/static/adm/js/api.js @@ -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"; diff --git a/server/src/uds/static/adm/js/gui-definition.js b/server/src/uds/static/adm/js/gui-definition.js index 8c2750448..03f016f30 100644 --- a/server/src/uds/static/adm/js/gui-definition.js +++ b/server/src/uds/static/adm/js/gui-definition.js @@ -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')), + }); }); diff --git a/server/src/uds/static/adm/js/gui-element.js b/server/src/uds/static/adm/js/gui-element.js index 381b1ebcb..8317d9486 100644 --- a/server/src/uds/static/adm/js/gui-element.js +++ b/server/src/uds/static/adm/js/gui-element.js @@ -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+' '+value.name+'', 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)); + }; + }, }; diff --git a/server/src/uds/static/adm/js/gui.js b/server/src/uds/static/adm/js/gui.js index 62e116a8a..858735bcb 100644 --- a/server/src/uds/static/adm/js/gui.js +++ b/server/src/uds/static/adm/js/gui.js @@ -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(); diff --git a/server/src/uds/templates/uds/admin/tmpl/modal.html b/server/src/uds/templates/uds/admin/tmpl/modal.html index ffae71abf..232319349 100644 --- a/server/src/uds/templates/uds/admin/tmpl/modal.html +++ b/server/src/uds/templates/uds/admin/tmpl/modal.html @@ -5,7 +5,7 @@