mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-08 21:18:00 +03:00
* Implemented services REST part
* Added possibility of creating own types for DetailApi (useful for provicers/services, where services is a detail but also has types) More refactoring
This commit is contained in:
parent
d46400c1f7
commit
7ea7086ba9
@ -15,6 +15,7 @@ encoding//src/uds/REST/methods/login_logout.py=utf-8
|
|||||||
encoding//src/uds/REST/methods/networks.py=utf-8
|
encoding//src/uds/REST/methods/networks.py=utf-8
|
||||||
encoding//src/uds/REST/methods/osmanagers.py=utf-8
|
encoding//src/uds/REST/methods/osmanagers.py=utf-8
|
||||||
encoding//src/uds/REST/methods/providers.py=utf-8
|
encoding//src/uds/REST/methods/providers.py=utf-8
|
||||||
|
encoding//src/uds/REST/methods/services.py=utf-8
|
||||||
encoding//src/uds/REST/methods/transports.py=utf-8
|
encoding//src/uds/REST/methods/transports.py=utf-8
|
||||||
encoding//src/uds/REST/methods/users_groups.py=utf-8
|
encoding//src/uds/REST/methods/users_groups.py=utf-8
|
||||||
encoding//src/uds/REST/mixins.py=utf-8
|
encoding//src/uds/REST/mixins.py=utf-8
|
||||||
|
@ -34,6 +34,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from uds.models import Provider
|
from uds.models import Provider
|
||||||
|
from services import Services
|
||||||
from uds.core import services
|
from uds.core import services
|
||||||
|
|
||||||
from uds.REST import Handler, NotFound
|
from uds.REST import Handler, NotFound
|
||||||
@ -46,12 +47,23 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Providers(ModelHandlerMixin, Handler):
|
class Providers(ModelHandlerMixin, Handler):
|
||||||
model = Provider
|
model = Provider
|
||||||
|
detail = { 'services': Services }
|
||||||
|
save_fields = ['name', 'comments']
|
||||||
|
|
||||||
def item_as_dict(self, provider):
|
def item_as_dict(self, provider):
|
||||||
type_ = provider.getType()
|
type_ = provider.getType()
|
||||||
|
|
||||||
|
# Icon can have a lot of data (1-2 Kbytes), but it's not expected to have a lot of services providers, and even so, this will work fine
|
||||||
|
offers = [{
|
||||||
|
'name' : ugettext(t.name()),
|
||||||
|
'type' : t.type(),
|
||||||
|
'description' : ugettext(t.description()),
|
||||||
|
'icon' : t.icon().replace('\n', '') } for t in type_.getServicesTypes()]
|
||||||
|
|
||||||
return { 'id': provider.id,
|
return { 'id': provider.id,
|
||||||
'name': provider.name,
|
'name': provider.name,
|
||||||
'services_count': provider.services.count(),
|
'services_count': provider.services.count(),
|
||||||
|
'offers': offers,
|
||||||
'type': type_.type(),
|
'type': type_.type(),
|
||||||
'comments': provider.comments,
|
'comments': provider.comments,
|
||||||
}
|
}
|
||||||
@ -64,13 +76,15 @@ class Types(ModelTypeHandlerMixin, Handler):
|
|||||||
|
|
||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
try:
|
try:
|
||||||
return services.factory().lookup(type_).guiDescription()
|
return self.addDefaultFields(services.factory().lookup(type_).guiDescription(), ['name', 'comments'])
|
||||||
except:
|
except:
|
||||||
raise NotFound('type not found')
|
raise NotFound('type not found')
|
||||||
|
|
||||||
class TableInfo(ModelTableHandlerMixin, Handler):
|
class TableInfo(ModelTableHandlerMixin, Handler):
|
||||||
path = 'providers'
|
path = 'providers'
|
||||||
|
detail = { 'services': Services }
|
||||||
title = _('Current service providers')
|
title = _('Current service providers')
|
||||||
|
|
||||||
fields = [
|
fields = [
|
||||||
{ 'name': {'title': _('Name'), 'type': 'iconType' } },
|
{ 'name': {'title': _('Name'), 'type': 'iconType' } },
|
||||||
{ 'comments': {'title': _('Comments')}},
|
{ 'comments': {'title': _('Comments')}},
|
||||||
|
84
server/src/uds/REST/methods/services.py
Normal file
84
server/src/uds/REST/methods/services.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 Virtual Cable S.L.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
# are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||||
|
# may be used to endorse or promote products derived from this software
|
||||||
|
# without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
'''
|
||||||
|
@provideror: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
|
'''
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from uds.models import Provider
|
||||||
|
|
||||||
|
from uds.REST.mixins import DetailHandler
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Services(DetailHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
# Extract providerenticator
|
||||||
|
provider = self._kwargs['parent']
|
||||||
|
|
||||||
|
try:
|
||||||
|
if len(self._args) == 0:
|
||||||
|
return [{
|
||||||
|
'id':k.id,
|
||||||
|
'name': k.name,
|
||||||
|
'comments': k.comments,
|
||||||
|
'type': k.data_type,
|
||||||
|
'typeName' : _(k.getType().name())
|
||||||
|
} for k in provider.services.all() ]
|
||||||
|
else:
|
||||||
|
with provider.get(pk=self._args[0]) as k:
|
||||||
|
return {
|
||||||
|
'id':k.id,
|
||||||
|
'name': k.name,
|
||||||
|
'comments': k.comments,
|
||||||
|
'type': k.data_type,
|
||||||
|
'typeName' : _(k.getType().name())
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
logger.exception('En services')
|
||||||
|
return { 'error': 'not found' }
|
||||||
|
|
||||||
|
def getTitle(self):
|
||||||
|
try:
|
||||||
|
return _('Services of {0}').format(Provider.objects.get(pk=self._kwargs['parent_id']).name)
|
||||||
|
except:
|
||||||
|
return _('Current services')
|
||||||
|
|
||||||
|
def getFields(self):
|
||||||
|
return [
|
||||||
|
{ 'name': {'title': _('Service name'), 'visible': True, 'type': 'iconType' } },
|
||||||
|
{ 'comments': { 'title': _('Comments') } },
|
||||||
|
{ 'type': {'title': _('Type') } }
|
||||||
|
]
|
@ -56,7 +56,7 @@ class Transports(ModelHandlerMixin, Handler):
|
|||||||
'comments': item.comments,
|
'comments': item.comments,
|
||||||
'priority': item.priority,
|
'priority': item.priority,
|
||||||
'nets_positive': item.nets_positive,
|
'nets_positive': item.nets_positive,
|
||||||
'networks': [ {'id': k.id} for k in item.networks.all() ],
|
'networks': [ n.id for n in item.networks.all() ],
|
||||||
'deployed_count': item.deployedServices.count(),
|
'deployed_count': item.deployedServices.count(),
|
||||||
'type': type_.type(),
|
'type': type_.type(),
|
||||||
}
|
}
|
||||||
@ -64,7 +64,6 @@ class Transports(ModelHandlerMixin, Handler):
|
|||||||
|
|
||||||
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()
|
||||||
|
@ -183,7 +183,6 @@ 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
|
||||||
|
@ -767,6 +767,10 @@ class UserInterface(object):
|
|||||||
val = '\001' + cPickle.dumps(v.value)
|
val = '\001' + cPickle.dumps(v.value)
|
||||||
else:
|
else:
|
||||||
val = v.value
|
val = v.value
|
||||||
|
if val is True:
|
||||||
|
val = gui.TRUE
|
||||||
|
elif val is False:
|
||||||
|
val = gui.FALSE
|
||||||
arr.append(k + '\003' + val)
|
arr.append(k + '\003' + val)
|
||||||
return '\002'.join(arr).encode('zip')
|
return '\002'.join(arr).encode('zip')
|
||||||
|
|
||||||
|
@ -59,12 +59,16 @@ table.dataTable tr.even td.sorting_3 { background-color: blue; }*/
|
|||||||
margin-bottom: 0.3em;
|
margin-bottom: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* modal dialogs & related*/
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
/* new custom width */
|
/* new custom width */
|
||||||
width: 60%;
|
width: 60%;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
.tooltip {
|
.tooltip {
|
||||||
z-index: 2014;
|
z-index: 2014;
|
||||||
}
|
}
|
||||||
|
@ -331,16 +331,18 @@ BasicModelRest.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
detail: function(id, child) {
|
detail: function(id, child, options) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return new DetailModelRestApi(this, id, child);
|
options = options || {};
|
||||||
|
return new DetailModelRestApi(this, id, child, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// For REST of type /auth/[id]/users, /services/[id]/users, ...
|
// For REST of type /auth/[id]/users, /services/[id]/users, ...
|
||||||
function DetailModelRestApi(parentApi, parentId, model) {
|
function DetailModelRestApi(parentApi, parentId, model, options) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
this.options = options;
|
||||||
this.base = new BasicModelRest(undefined, {
|
this.base = new BasicModelRest(undefined, {
|
||||||
getPath: [parentApi.path, parentId, model].join('/'),
|
getPath: [parentApi.path, parentId, model].join('/'),
|
||||||
typesPath: '.', // We do not has this on details
|
typesPath: '.', // We do not has this on details
|
||||||
@ -350,29 +352,33 @@ function DetailModelRestApi(parentApi, parentId, model) {
|
|||||||
|
|
||||||
DetailModelRestApi.prototype = {
|
DetailModelRestApi.prototype = {
|
||||||
// Generates a basic model with fixed methods for "detail" models
|
// Generates a basic model with fixed methods for "detail" models
|
||||||
get: function(options) {
|
get: function(success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return this.base.get(options);
|
return this.base.get(success_fnc, fail_fnc);
|
||||||
},
|
},
|
||||||
tableInfo: function(options) {
|
tableInfo: function(success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return this.base.tableInfo(options);
|
return this.base.tableInfo(success_fnc, fail_fnc);
|
||||||
},
|
},
|
||||||
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";
|
||||||
return this.base.list(success_fnc, options);
|
return this.base.list(success_fnc, fail_fnc);
|
||||||
},
|
},
|
||||||
overview: function(success_fnc, options) {
|
overview: function(success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return this.base.overview(success_fnc, options);
|
return this.base.overview(success_fnc, fail_fnc);
|
||||||
},
|
},
|
||||||
item: function(itemId, success_fnc, options) {
|
item: function(itemId, success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return this.base.item(success_fnc, options);
|
return this.base.item(success_fnc, fail_fnc);
|
||||||
},
|
},
|
||||||
types: function(options) {
|
types: function(success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return this.base.types(options);
|
if( this.options.types ) {
|
||||||
|
this.options.types(success_fnc, fail_fnc);
|
||||||
|
} else {
|
||||||
|
return this.base.types(success_fnc, fail_fnc);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -29,22 +29,60 @@ gui.dashboard.link = function(event) {
|
|||||||
gui.providers = new GuiElement(api.providers, 'provi');
|
gui.providers = new GuiElement(api.providers, 'provi');
|
||||||
gui.providers.link = function(event) {
|
gui.providers.link = function(event) {
|
||||||
"use strict";
|
"use strict";
|
||||||
gui.clearWorkspace();
|
api.templates.get('providers', function(tmpl) {
|
||||||
gui.appendToWorkspace(gui.breadcrumbs(gettext('Service Providers')));
|
gui.clearWorkspace();
|
||||||
|
gui.appendToWorkspace(api.templates.evaluate(tmpl, {
|
||||||
|
providers : 'providers-placeholder',
|
||||||
|
services : 'services-placeholder',
|
||||||
|
}));
|
||||||
|
gui.setLinksEvents();
|
||||||
|
|
||||||
var tableId = gui.providers.table({
|
var tableId = gui.providers.table({
|
||||||
rowSelect : 'single',
|
container : 'providers-placeholder',
|
||||||
onEdit: function(value, event, table) {
|
rowSelect : 'single',
|
||||||
gui.providers.rest.gui(value.type, function(data) {
|
onRowSelect : function(selected) {
|
||||||
var form = gui.fields(data);
|
api.tools.blockUI();
|
||||||
gui.appendToWorkspace(gui.modal('edit_modal', gettext('Edit service provider'), form));
|
gui.doLog(selected[0]);
|
||||||
$('#edit_modal').modal()
|
var id = selected[0].id;
|
||||||
.on('hidden.bs.modal', function () {
|
// Options for detail, to initialize types correctly
|
||||||
$('#edit_modal').remove();
|
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);
|
||||||
|
|
||||||
|
services.table({
|
||||||
|
container : 'services-placeholder',
|
||||||
|
rowSelect : 'single',
|
||||||
|
buttons : [ 'new', 'edit', 'delete', 'xls' ],
|
||||||
|
scrollToTable : false,
|
||||||
|
onLoad: function(k) {
|
||||||
|
api.tools.unblockUI();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
buttons : [ 'edit', 'delete', 'xls' ],
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -157,9 +195,8 @@ gui.connectivity.link = function(event) {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
var form = gui.form.fromFields(tabs, item);
|
gui.forms.launchModal(gettext('Edit transport')+' '+value.name, tabs, item, function(form_selector, closeFnc) {
|
||||||
gui.launchModalForm(gettext('Edit transport')+' '+value.name,form, function(form_selector, closeFnc) {
|
var fields = gui.forms.read(form_selector);
|
||||||
var fields = gui.form.read(form_selector);
|
|
||||||
fields.data_type = value.type;
|
fields.data_type = value.type;
|
||||||
fields.nets_positive = false;
|
fields.nets_positive = false;
|
||||||
gui.connectivity.transports.rest.save(fields, function(data) { // Success on put
|
gui.connectivity.transports.rest.save(fields, function(data) { // Success on put
|
||||||
@ -182,12 +219,17 @@ gui.connectivity.link = function(event) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Networks',
|
title: 'Networks',
|
||||||
fields: [],
|
fields: [
|
||||||
|
gui.forms.guiField('networks', 'multichoice', gettext('Available for networks'),
|
||||||
|
gettext('Select networks that will see this transport'), [], []),
|
||||||
|
gui.forms.guiField('nets_positive', 'checkbox', gettext('Transport active for selected networks'),
|
||||||
|
gettext('If active, transport will only be available on selected networks. If inactive, transport will be available form any net EXCEPT selected networks'),
|
||||||
|
true)
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
var form = gui.form.fromFields(tabs);
|
gui.forms.launchModal(gettext('New transport'), tabs, undefined, function(form_selector, closeFnc) {
|
||||||
gui.launchModalForm(gettext('New transport'), form, function(form_selector, closeFnc) {
|
|
||||||
var fields = gui.form.read(form_selector);
|
var fields = gui.form.read(form_selector);
|
||||||
// Append "own" fields, in this case data_type
|
// Append "own" fields, in this case data_type
|
||||||
fields.data_type = type;
|
fields.data_type = type;
|
||||||
|
@ -5,7 +5,7 @@ function BasicGuiElement(name) {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function GuiElement(restItem, name) {
|
function GuiElement(restItem, name, typesFunction) {
|
||||||
"use strict";
|
"use strict";
|
||||||
this.rest = restItem;
|
this.rest = restItem;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -22,6 +22,7 @@ GuiElement.prototype = {
|
|||||||
var self = this;
|
var self = this;
|
||||||
this.rest.types(function(data) {
|
this.rest.types(function(data) {
|
||||||
var styles = '';
|
var styles = '';
|
||||||
|
var alreadyAttached = $('#gui-style-'+self.name).length !== 0;
|
||||||
$.each(data, function(index, value) {
|
$.each(data, function(index, value) {
|
||||||
var className = self.name + '-' + value.type;
|
var className = self.name + '-' + value.type;
|
||||||
self.types[value.type] = {
|
self.types[value.type] = {
|
||||||
@ -30,12 +31,15 @@ GuiElement.prototype = {
|
|||||||
description : value.description || ''
|
description : value.description || ''
|
||||||
};
|
};
|
||||||
gui.doLog('Creating style for ' + className);
|
gui.doLog('Creating style for ' + className);
|
||||||
var style = '.' + className + ' { display:inline-block; background: url(data:image/png;base64,' +
|
if( !alreadyAttached ) {
|
||||||
value.icon + '); ' + 'width: 16px; height: 16px; vertical-align: middle; } ';
|
var style = '.' + className + ' { display:inline-block; background: url(data:image/png;base64,' +
|
||||||
styles += style;
|
value.icon + '); ' + 'width: 16px; height: 16px; vertical-align: middle; } ';
|
||||||
|
styles += style;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (styles !== '') {
|
if (styles !== '') {
|
||||||
styles = '<style media="screen">' + styles + '</style>';
|
// If style already attached, do not re-attach it
|
||||||
|
styles = '<style id="gui-style-' + self.name + '" media="screen">' + styles + '</style>';
|
||||||
$(styles).appendTo('head');
|
$(styles).appendTo('head');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -101,7 +105,8 @@ GuiElement.prototype = {
|
|||||||
// Icon renderer, based on type (created on init methods in styles)
|
// Icon renderer, based on type (created on init methods in styles)
|
||||||
var renderTypeIcon = function(data, type, value){
|
var renderTypeIcon = function(data, type, value){
|
||||||
if( type == 'display' ) {
|
if( type == 'display' ) {
|
||||||
var css = self.types[value.type].css;
|
self.types[value.type] = self.types[value.type] || {}
|
||||||
|
var css = self.types[value.type].css || 'fa fa-asterisk';
|
||||||
return '<span class="' + css + '"></span> ' + renderEmptyCell(data);
|
return '<span class="' + css + '"></span> ' + renderEmptyCell(data);
|
||||||
} else {
|
} else {
|
||||||
return renderEmptyCell(data);
|
return renderEmptyCell(data);
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
(function(gui, $, undefined) {
|
(function(gui, $, undefined) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Returns a form that will manage a gui description (new or edit)
|
gui.forms = {};
|
||||||
gui.fieldsToHtml = function(itemGui, item, editing) {
|
|
||||||
|
// Returns form fields that will manage a gui description (new or edit)
|
||||||
|
gui.forms.fieldsToHtml = function(itemGui, item, editing) {
|
||||||
var html = '';
|
var html = '';
|
||||||
// itemGui is expected to have fields sorted by .gui.order (REST api returns them sorted)
|
// itemGui is expected to have fields sorted by .gui.order (REST api returns them sorted)
|
||||||
$.each(itemGui, function(index, f){
|
$.each(itemGui, function(index, f){
|
||||||
@ -30,9 +32,7 @@
|
|||||||
return html;
|
return html;
|
||||||
};
|
};
|
||||||
|
|
||||||
gui.form = {};
|
gui.forms.fromFields = function(fields, item) {
|
||||||
|
|
||||||
gui.form.fromFields = function(fields, item) {
|
|
||||||
var editing = item !== undefined; // Locate real Editing
|
var editing = item !== undefined; // Locate real Editing
|
||||||
item = item || {id:''};
|
item = item || {id:''};
|
||||||
var form = '<form class="form-horizontal" role="form">' +
|
var form = '<form class="form-horizontal" role="form">' +
|
||||||
@ -43,20 +43,20 @@
|
|||||||
var tabsContent = [];
|
var tabsContent = [];
|
||||||
var active = ' active in' ;
|
var active = ' active in' ;
|
||||||
$.each(fields.tabs, function(index, tab){
|
$.each(fields.tabs, function(index, tab){
|
||||||
tabsContent.push('<div class="tab-pane fade' + active + '" id="' + id + index + '">' + gui.fieldsToHtml(tab.fields, item) + '</div>' );
|
tabsContent.push('<div class="tab-pane fade' + active + '" id="' + id + index + '">' + gui.forms.fieldsToHtml(tab.fields, item) + '</div>' );
|
||||||
tabs.push('<li><a href="#' + id + index + '" data-toggle="tab">' + tab.title + '</a></li>' );
|
tabs.push('<li><a href="#' + id + index + '" data-toggle="tab">' + tab.title + '</a></li>' );
|
||||||
active = '';
|
active = '';
|
||||||
});
|
});
|
||||||
form += '<ul class="nav nav-tabs">' + tabs.join('\n') + '</ul><div class="tab-content">' + tabsContent.join('\n') + '</div>';
|
form += '<ul class="nav nav-tabs">' + tabs.join('\n') + '</ul><div class="tab-content">' + tabsContent.join('\n') + '</div>';
|
||||||
} else {
|
} else {
|
||||||
form += gui.fieldsToHtml(fields, item, editing);
|
form += gui.forms.fieldsToHtml(fields, item, editing);
|
||||||
}
|
}
|
||||||
form += '</form>';
|
form += '</form>';
|
||||||
return form;
|
return form;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reads fields from a form
|
// Reads fields from a form
|
||||||
gui.form.read = function(formSelector) {
|
gui.forms.read = function(formSelector) {
|
||||||
var res = {};
|
var res = {};
|
||||||
$(formSelector + ' .modal_field_data').each(function(i, field) {
|
$(formSelector + ' .modal_field_data').each(function(i, field) {
|
||||||
var $field = $(field);
|
var $field = $(field);
|
||||||
@ -72,6 +72,82 @@
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gui.forms.launchModal = function(title, fields, item, onSuccess) {
|
||||||
|
var id = 'modal-' + Math.random().toString().split('.')[1]; // Get a random ID for this modal
|
||||||
|
gui.appendToWorkspace(gui.modal(id, title, gui.forms.fromFields(fields, item)));
|
||||||
|
id = '#' + id; // for jQuery
|
||||||
|
|
||||||
|
// Get form
|
||||||
|
var $form = $(id + ' form');
|
||||||
|
|
||||||
|
// For "beauty" switches, initialize them now
|
||||||
|
$(id + ' .make-switch').bootstrapSwitch();
|
||||||
|
// Activate "cool" selects
|
||||||
|
$(id + ' .selectpicker').selectpicker();
|
||||||
|
// TEST: cooller on mobile devices
|
||||||
|
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
|
||||||
|
$(id + ' .selectpicker').selectpicker('mobile');
|
||||||
|
}
|
||||||
|
// Activate tooltips
|
||||||
|
$(id + ' [data-toggle="tooltip"]').tooltip({delay: {show: 1000, hide: 100}, placement: 'auto right'});
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
$form.validate({
|
||||||
|
debug: true,
|
||||||
|
errorClass: 'text-danger',
|
||||||
|
validClass: 'has-success',
|
||||||
|
highlight: function(element) {
|
||||||
|
$(element).closest('.form-group').addClass('has-error');
|
||||||
|
},
|
||||||
|
success: function(element) {
|
||||||
|
$(element).closest('.form-group').removeClass('has-error');
|
||||||
|
$(element).remove();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// And catch "accept" (default is "Save" in fact) button click
|
||||||
|
$(id + ' .button-accept').click(function(){
|
||||||
|
if( !$form.valid() )
|
||||||
|
return;
|
||||||
|
if( onSuccess ) {
|
||||||
|
onSuccess(id + ' form', function(){$(id).modal('hide');}); // Delegate close to to onSuccess
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$(id).modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Launch modal
|
||||||
|
$(id).modal({keyboard: false})
|
||||||
|
.on('hidden.bs.modal', function () {
|
||||||
|
$(id).remove();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// simple gui generators
|
||||||
|
gui.forms.guiField = function(name, type, label, tooltip, value, values, length, multiline, readonly, required) {
|
||||||
|
length = length || 128;
|
||||||
|
multiline = multiline !== undefined ? multiline : 0;
|
||||||
|
readonly = readonly || false;
|
||||||
|
required = required || false;
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
gui: {
|
||||||
|
defvalue: value,
|
||||||
|
value: value,
|
||||||
|
values: values,
|
||||||
|
label: label,
|
||||||
|
length: length,
|
||||||
|
multiline: multiline,
|
||||||
|
rdonly: readonly, // rdonly applies just to editing
|
||||||
|
required: required,
|
||||||
|
tooltip: tooltip,
|
||||||
|
type: type,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}(window.gui = window.gui || {}, jQuery));
|
}(window.gui = window.gui || {}, jQuery));
|
||||||
|
|
||||||
|
@ -118,58 +118,6 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
gui.launchModalForm = function(title, form, onSuccess) {
|
|
||||||
var id = 'modal-' + Math.random().toString().split('.')[1]; // Get a random ID for this modal
|
|
||||||
gui.appendToWorkspace(gui.modal(id, title, form));
|
|
||||||
id = '#' + id; // for jQuery
|
|
||||||
|
|
||||||
// Get form
|
|
||||||
var $form = $(id + ' form');
|
|
||||||
|
|
||||||
// For "beauty" switches, initialize them now
|
|
||||||
$(id + ' .make-switch').bootstrapSwitch();
|
|
||||||
// Activate "cool" selects
|
|
||||||
$(id + ' .selectpicker').selectpicker();
|
|
||||||
// TEST: cooller on mobile devices
|
|
||||||
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
|
|
||||||
$(id + ' .selectpicker').selectpicker('mobile');
|
|
||||||
}
|
|
||||||
// Activate tooltips
|
|
||||||
$(id + ' [data-toggle="tooltip"]').tooltip({delay: {show: 1000, hide: 100}, placement: 'auto right'});
|
|
||||||
|
|
||||||
// Validation
|
|
||||||
$form.validate({
|
|
||||||
debug: true,
|
|
||||||
errorClass: 'text-danger',
|
|
||||||
validClass: 'has-success',
|
|
||||||
highlight: function(element) {
|
|
||||||
$(element).closest('.form-group').addClass('has-error');
|
|
||||||
},
|
|
||||||
success: function(element) {
|
|
||||||
$(element).closest('.form-group').removeClass('has-error');
|
|
||||||
$(element).remove();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// And catch "accept" (default is "Save" in fact) button click
|
|
||||||
$(id + ' .button-accept').click(function(){
|
|
||||||
if( !$form.valid() )
|
|
||||||
return;
|
|
||||||
if( onSuccess ) {
|
|
||||||
onSuccess(id + ' form', function(){$(id).modal('hide');}); // Delegate close to to onSuccess
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
$(id).modal('hide');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Launch modal
|
|
||||||
$(id).modal({keyboard: false})
|
|
||||||
.on('hidden.bs.modal', function () {
|
|
||||||
$(id).remove();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
gui.failRequestMessageFnc = function(jqXHR, textStatus, errorThrown) {
|
gui.failRequestMessageFnc = function(jqXHR, textStatus, errorThrown) {
|
||||||
api.templates.get('request_failed', function(tmpl) {
|
api.templates.get('request_failed', function(tmpl) {
|
||||||
|
18
server/src/uds/templates/uds/admin/tmpl/providers.html
Normal file
18
server/src/uds/templates/uds/admin/tmpl/providers.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h1>{% trans 'Providers' %}</h1>
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a class="lnk-dashboard" href="#"><i class="fa fa-dashboard"></i> Dashboard</a></li>
|
||||||
|
<li>{% trans 'Providers' %}</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.row -->
|
||||||
|
{% verbatim %}
|
||||||
|
<div class="row">
|
||||||
|
<div id="{{ providers }}" class="col-xs-12"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div id="{{ services }}" class="col-xs-12"></div>
|
||||||
|
</div>
|
||||||
|
{% endverbatim %}
|
Loading…
Reference in New Issue
Block a user