mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-23 17:34:17 +03:00
Implemented callbacks for gui "fillers", that is, choices that calls server for information about how to fill other fields with returned values...
This commit is contained in:
parent
36bdbbfd37
commit
61c9f4f975
@ -11,6 +11,7 @@ encoding//src/server/urls.py=utf-8
|
||||
encoding//src/uds/REST/__init__.py=utf-8
|
||||
encoding//src/uds/REST/handlers.py=utf-8
|
||||
encoding//src/uds/REST/methods/authenticators.py=utf-8
|
||||
encoding//src/uds/REST/methods/gui_callback.py=utf-8
|
||||
encoding//src/uds/REST/methods/login_logout.py=utf-8
|
||||
encoding//src/uds/REST/methods/networks.py=utf-8
|
||||
encoding//src/uds/REST/methods/osmanagers.py=utf-8
|
||||
@ -151,6 +152,7 @@ encoding//src/uds/migrations/0012_auto__add_field_authenticator_small_name.py=ut
|
||||
encoding//src/uds/migrations/0013_auto__add_field_group_is_meta__add_field_uniqueid_stamp.py=utf-8
|
||||
encoding//src/uds/migrations/0014_auto__add_field_network_net_string.py=utf-8
|
||||
encoding//src/uds/migrations/0016_auto__add_field_userservice_cluster_node.py=utf-8
|
||||
encoding//src/uds/migrations/0017_change_tables.py=utf-8
|
||||
encoding//src/uds/models.py=utf-8
|
||||
encoding//src/uds/osmanagers/LinuxOsManager/LinuxOsManager.py=utf-8
|
||||
encoding//src/uds/osmanagers/LinuxOsManager/__init__.py=utf-8
|
||||
|
75
server/src/uds/REST/methods/gui_callback.py
Normal file
75
server/src/uds/REST/methods/gui_callback.py
Normal file
@ -0,0 +1,75 @@
|
||||
# -*- 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.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from uds.core.ui.UserInterface import gui
|
||||
from uds.REST import Handler, RequestError, NotFound
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Enclosed methods under /auth path
|
||||
|
||||
class Callback(Handler):
|
||||
path = 'gui'
|
||||
authenticated = True # Public method
|
||||
needs_staff = True #
|
||||
|
||||
def get(self):
|
||||
'''
|
||||
This login uses parameters to generate auth token
|
||||
The alternative is to use the template tag inside "REST" that is called auth_token, that extracts an auth token from an user session
|
||||
We can use any of this forms due to the fact that the auth token is in fact a session key
|
||||
Parameters:
|
||||
mandatory:
|
||||
username:
|
||||
password:
|
||||
auth:
|
||||
optional:
|
||||
locale: (defaults to "en")
|
||||
Result:
|
||||
on success: { 'result': 'ok', 'auth': [auth_code] }
|
||||
on error: { 'result: 'error', 'error': [error string] }
|
||||
'''
|
||||
|
||||
logger.debug('Params: {0}'.format(self._params))
|
||||
|
||||
if len(self._args) != 1:
|
||||
raise RequestError('Invalid Request')
|
||||
|
||||
if gui.callbacks.has_key(self._args[0]):
|
||||
return gui.callbacks[self._args[0]](self._params)
|
||||
|
||||
raise NotFound('callback {0} not found'.format(self._args[0]))
|
||||
|
@ -50,6 +50,13 @@ class ContentProcessor(object):
|
||||
|
||||
def __init__(self, request):
|
||||
self._request = request
|
||||
|
||||
def processGetParameters(self):
|
||||
if self._request.method != 'GET':
|
||||
return {}
|
||||
|
||||
return self._request.GET.copy()
|
||||
|
||||
|
||||
def processParameters(self):
|
||||
return ''
|
||||
@ -70,7 +77,7 @@ class JsonProcessor(ContentProcessor):
|
||||
def processParameters(self):
|
||||
try:
|
||||
if len(self._request.body) == 0:
|
||||
return {}
|
||||
return self.processGetParameters()
|
||||
res = json.loads(self._request.body)
|
||||
logger.debug(res)
|
||||
return res
|
||||
|
@ -66,7 +66,7 @@ table.dataTable tr.even td.sorting_3 { background-color: blue; }*/
|
||||
/* modal dialogs & related*/
|
||||
.modal-dialog {
|
||||
/* new custom width */
|
||||
width: 60%;
|
||||
width: 90%;
|
||||
|
||||
}
|
||||
|
||||
@ -76,6 +76,7 @@ table.dataTable tr.even td.sorting_3 { background-color: blue; }*/
|
||||
.tooltip {
|
||||
z-index: 2014;
|
||||
}
|
||||
|
||||
/* Edit Below to Customize Widths > 768px */
|
||||
@media (min-width:768px) {
|
||||
|
||||
@ -142,6 +143,11 @@ table.dataTable tr.even td.sorting_3 { background-color: blue; }*/
|
||||
.label-tbl-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
/* new custom width */
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ function BasicModelRest(path, options) {
|
||||
|
||||
BasicModelRest.prototype = {
|
||||
// options:
|
||||
// cacheKey: '.' --> do not cache
|
||||
// cacheKey: '.' --> do not cache (undefined will set cacheKey to current path)
|
||||
// undefined -- > use path as key
|
||||
// success: success fnc to execute in case of success
|
||||
_requestPath: function(path, options) {
|
||||
@ -322,7 +322,7 @@ BasicModelRest.prototype = {
|
||||
path = this.guiPath;
|
||||
}
|
||||
return this._requestPath(path, {
|
||||
cacheKey: path,
|
||||
cacheKey: '.', // Gui is not cacheable, it's dynamic and can change from call to call
|
||||
success: success_fnc,
|
||||
fail: fail_fnc,
|
||||
});
|
||||
|
@ -4,9 +4,24 @@
|
||||
|
||||
gui.forms = {};
|
||||
|
||||
gui.forms.callback = function(formSelector, method, params, success_fnc) {
|
||||
var path = 'gui/callback/' + method;
|
||||
var p = [];
|
||||
$.each(params, function(index, val) {
|
||||
p.push(val.name + '=' + encodeURIComponent(val.value));
|
||||
});
|
||||
path = path + '?' + p.join('&');
|
||||
api.getJson(path, {
|
||||
success: success_fnc,
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Returns form fields that will manage a gui description (new or edit)
|
||||
gui.forms.fieldsToHtml = function(itemGui, item, editing) {
|
||||
var html = '';
|
||||
var fillers = []; // Fillers (callbacks)
|
||||
var originalValues = {}; // Initial stored values (defaults to "reset" form and also used on fillers callback to try to restore previous value)
|
||||
// itemGui is expected to have fields sorted by .gui.order (REST api returns them sorted)
|
||||
$.each(itemGui, function(index, f){
|
||||
gui.doLog(f);
|
||||
@ -15,8 +30,19 @@
|
||||
if( f.gui.type == 'text' && f.gui.multiline ) {
|
||||
f.gui.type = 'textbox';
|
||||
}
|
||||
var value = item[f.name] || f.gui.value || f.gui.defvalue;
|
||||
// We need to convert "array" values for multichoices to single list of ids (much more usable right here)
|
||||
if( f.gui.type == 'multichoice') {
|
||||
var newValue = [];
|
||||
$.each(value, function(undefined, val) {
|
||||
newValue.push(val.id);
|
||||
});
|
||||
value = newValue;
|
||||
}
|
||||
|
||||
originalValues[f.name] = value; // Store original value
|
||||
html += api.templates.evaluate('tmpl_fld_'+f.gui.type, {
|
||||
value: item[f.name] || f.gui.value || f.gui.defvalue, // If no value present, use default value
|
||||
value: value, // If no value present, use default value
|
||||
values: f.gui.values,
|
||||
label: f.gui.label,
|
||||
length: f.gui.length,
|
||||
@ -28,31 +54,106 @@
|
||||
name: f.name,
|
||||
css: 'modal_field_data',
|
||||
});
|
||||
|
||||
// if this field has a filler (callback to get data)
|
||||
if( f.gui.fills ) {
|
||||
gui.doLog('This field has a filler');
|
||||
fillers.push({ name: f.name, callbackName: f.gui.fills.callbackName, parameters: f.gui.fills.parameters });
|
||||
}
|
||||
|
||||
});
|
||||
return html;
|
||||
return { html: html, fillers: fillers, originalValues: originalValues };
|
||||
};
|
||||
|
||||
gui.forms.fromFields = function(fields, item) {
|
||||
var editing = item !== undefined; // Locate real Editing
|
||||
item = item || {id:''};
|
||||
|
||||
var form = '<form class="form-horizontal" role="form">' +
|
||||
'<input type="hidden" name="id" class="modal_field_data" value="' + item.id + '">';
|
||||
var fillers = [];
|
||||
var originalValues = {};
|
||||
|
||||
if( fields.tabs ) {
|
||||
var id = 'tab-' + Math.random().toString().split('.')[1]; // Get a random base ID for tab entries
|
||||
var tabs = [];
|
||||
var tabsContent = [];
|
||||
var active = ' active in' ;
|
||||
$.each(fields.tabs, function(index, tab){
|
||||
tabsContent.push('<div class="tab-pane fade' + active + '" id="' + id + index + '">' + gui.forms.fieldsToHtml(tab.fields, item) + '</div>' );
|
||||
var h = gui.forms.fieldsToHtml(tab.fields, item);
|
||||
tabsContent.push('<div class="tab-pane fade' + active + '" id="' + id + index + '">' + h.html + '</div>' );
|
||||
tabs.push('<li><a href="#' + id + index + '" data-toggle="tab">' + tab.title + '</a></li>' );
|
||||
active = '';
|
||||
fillers = fillers.concat(h.fillers); // Fillers (callback based)
|
||||
$.extend(originalValues, h.originalValues); // Original values
|
||||
gui.doLog('Fillers:', h.fillers);
|
||||
});
|
||||
form += '<ul class="nav nav-tabs">' + tabs.join('\n') + '</ul><div class="tab-content">' + tabsContent.join('\n') + '</div>';
|
||||
} else {
|
||||
form += gui.forms.fieldsToHtml(fields, item, editing);
|
||||
var h = gui.forms.fieldsToHtml(fields, item, editing);
|
||||
form += h.html;
|
||||
fillers = fillers.concat(h.fillers);
|
||||
$.extend(originalValues, h.originalValues);
|
||||
}
|
||||
form += '</form>';
|
||||
return form;
|
||||
|
||||
gui.doLog('Original values: ', originalValues);
|
||||
|
||||
// Init function for callbacks.
|
||||
// Callbacks can only be attached to "Selects", but it's parameters can be got from any field
|
||||
// This needs the "form selector" as base for setting callbacks, etc..
|
||||
var init = function(formSelector) {
|
||||
gui.doLog(formSelector, fillers);
|
||||
|
||||
/*var pos = 0;
|
||||
var triggerChangeSequentially = function() {
|
||||
if( pos >= fillers.length )
|
||||
return;
|
||||
$(formSelector + ' [name="' + fillers[pos].name + '"]').trigger('change');
|
||||
pos = pos + 1;
|
||||
};*/
|
||||
|
||||
var onChange = function(filler) {
|
||||
return function() {
|
||||
gui.doLog('Onchange invoked for ', filler);
|
||||
// Attach on change method to each filler, and after that, all
|
||||
var params = [];
|
||||
$.each(filler.parameters, function(undefined, p){
|
||||
var val = $(formSelector + ' [name="' + p + '"]').val();
|
||||
params.push({name: p, value: val});
|
||||
});
|
||||
gui.forms.callback(formSelector, filler.callbackName, params, function(data){
|
||||
$.each(data, function(undefined, sel){
|
||||
// Update select contents with returned values
|
||||
var $select = $(formSelector + ' [name="' + sel.name + '"]');
|
||||
|
||||
$select.empty();
|
||||
$.each(sel.values, function(undefined, value){
|
||||
$select.append('<option value="' + value.id + '">' + value.text + '</option>');
|
||||
});
|
||||
$select.val(originalValues[sel.name]);
|
||||
// Refresh selectpicker updated
|
||||
if($select.hasClass('selectpicker'))
|
||||
$select.selectpicker('refresh');
|
||||
// Trigger change for the changed item
|
||||
$select.trigger('change');
|
||||
|
||||
});
|
||||
//triggerChangeSequentially();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// Sets the "on change" event for select with fillers (callbacks that fills other fields)
|
||||
$.each(fillers, function(undefined, f) {
|
||||
$(formSelector + ' [name="' + f.name + '"]').on('change', onChange(f));
|
||||
});
|
||||
|
||||
if( fillers.length )
|
||||
$(formSelector + ' [name="' + fillers[0].name + '"]').trigger('change');
|
||||
};
|
||||
|
||||
return { 'html': form, 'init': init }; // Returns the form and a initialization function for the form, that must be invoked to start it
|
||||
};
|
||||
|
||||
// Reads fields from a form
|
||||
@ -74,9 +175,13 @@
|
||||
|
||||
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)));
|
||||
var ff = gui.forms.fromFields(fields, item);
|
||||
gui.appendToWorkspace(gui.modal(id, title, ff.html));
|
||||
id = '#' + id; // for jQuery
|
||||
|
||||
if( ff.init )
|
||||
ff.init(id);
|
||||
|
||||
// Get form
|
||||
var $form = $(id + ' form');
|
||||
|
||||
|
@ -128,22 +128,12 @@
|
||||
});
|
||||
};
|
||||
|
||||
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.failRequestModalFnc = function(title) {
|
||||
return function(jqXHR, textStatus, errorThrown) { // fail on put
|
||||
gui.launchModal(title, jqXHR.responseText, ' ');
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
gui.clearWorkspace = function() {
|
||||
$('#content').empty();
|
||||
$('#minimized').empty();
|
||||
|
@ -110,7 +110,7 @@
|
||||
// Initialize gui
|
||||
gui.init();
|
||||
// set default error function
|
||||
api.defaultFail = gui.failRequestMessageFnc;
|
||||
api.defaultFail = gui.failRequestModalFnc(gettext('Error on request'));
|
||||
});
|
||||
</script>
|
||||
{% block js %}{% endblock %}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% extends "uds/admin/tmpl/fld/form-group.html" %}
|
||||
{% load i18n %}
|
||||
{% comment %}The choice item MUST be a Select{% endcomment %}
|
||||
{% block field %}
|
||||
{% verbatim %}
|
||||
<select class="selectpicker show-menu-arrow {{ css }}" name="{{ name }}" id="{{ name }}_field" {{# if readonly }} disabled{{/ if }}>
|
||||
|
@ -62,9 +62,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% ifbrowser webkit %}
|
||||
webkit!!!
|
||||
{% endifbrowser %}
|
||||
<h2>{% trans "Services" %}</h2>
|
||||
<div class="clearfix">
|
||||
{% for ser in services %}
|
||||
|
Loading…
Reference in New Issue
Block a user