Advanced a lot with "generic model editing"

This commit is contained in:
Adolfo Gómez 2013-11-22 02:22:41 +00:00
parent 004e060aaf
commit de4a9ff291
24 changed files with 2837 additions and 91 deletions

View File

@ -36,7 +36,7 @@ from django.utils.translation import ugettext_lazy as _
from uds.models import Transport
from uds.core.transports import factory
from uds.REST import Handler, HandlerError
from uds.REST import Handler, NotFound
from uds.REST.mixins import ModelHandlerMixin, ModelTypeHandlerMixin, ModelTableHandlerMixin
import logging
@ -52,10 +52,11 @@ class Transports(ModelHandlerMixin, Handler):
type_ = item.getType()
return { 'id': item.id,
'name': item.name,
'comments': item.comments,
'priority': item.priority,
'nets_positive': item.nets_positive,
'deployed_count': item.deployedServices.count(),
'type': type_.type(),
'comments': item.comments
}
class Types(ModelTypeHandlerMixin, Handler):
@ -63,6 +64,13 @@ class Types(ModelTypeHandlerMixin, Handler):
def enum_types(self):
return factory().providers().values()
def getGui(self, type_):
try:
return factory().lookup(type_).guiDescription()
except:
raise NotFound('type not found')
class TableInfo(ModelTableHandlerMixin, Handler):
path = 'transports'

View File

@ -98,7 +98,12 @@ class ModelHandlerMixin(object):
return self.processDetail()
try:
return list(self.getItems(pk=self._args[0]))[0]
val = self.model.objects.get(pk=self._args[0])
res = self.item_as_dict(val)
if hasattr(val, 'getInstance'):
for key, value in val.getInstance().valuesDict().iteritems():
res[key] = value
return res
except:
raise NotFound('item not found')

View File

@ -20,7 +20,6 @@ body {
padding: 5px 15px;
}
/* Custom */
.btn3d-tables {
margin-top:0px;
@ -37,8 +36,8 @@ body {
}
/* collapsable && closeable pannels */
.chevron:before {
content: "\f139";
.chevron:before {
content: "\f139";
}
.chevron.collapsed:before {
content: "\f13a";
@ -53,6 +52,12 @@ body {
margin-bottom: 0.3em;
}
.modal-dialog {
/* new custom width */
width: 60%;
}
/* Edit Below to Customize Widths > 768px */
@media (min-width:768px) {

View File

@ -1,11 +1,26 @@
/* jshint strict: true */
// -------------------------------
// Templates related
// Inserted into api
// for the admin app
// -------------------------------
(function(api, $) {
api.templates = {};
"use strict";
// Registers Handlebar useful helpers
// Equal comparision (like if helper, but with comparation)
Handlebars.registerHelper('ifequals', function(context1, context2, options) {
console.log('Comparing ', context1, ' with ', context2);
if(context1 == context2) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
api.templates = {};
// Now initialize templates api
api.templates.cache = new api.cache('tmpls'); // Will cache templates locally. If name contains
// '?', data will not be cached and always
// re-requested. We do not care about lang, because page will reload on language change
@ -28,7 +43,7 @@
url : api.template_url + name,
type : "GET",
dataType : "text",
success : function(data) {
success : function(data) {
var cachedId = 'tmpl_' + name;
$this.cache.put('_' + cachedId, $this.evaluate(data));
$this.cache.put(name, cachedId);
@ -44,9 +59,8 @@
});
};
// Simple JavaScript Templating
// Based on John Resig - http://ejohn.org/ - MIT Licensed
api.templates.evaluate = function (str, data) {
// Simple JavaScript Templating, using HandleBars
api.templates.evaluate = function (str, context) {
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var cached;
@ -55,24 +69,9 @@
if( cached === undefined ) {
cached = api.templates.evaluate(document.getElementById(str).innerHTML);
this.cache.put('_'+str, cached);
}
}
}
// If cached, get cached first
var fn = cached ||
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str.replace(/[\r\t\n]/g, " ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g, "$1\r").replace(
/\t=(.*?)%>/g, "',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join(
"\\'") + "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn(data) : fn;
var template = cached || Handlebars.compile(str);
return context ? template(context) : template;
};
}(window.api = window.api || {}, jQuery));

View File

@ -33,7 +33,7 @@ gui.providers.link = function(event) {
gui.appendToWorkspace(gui.breadcrumbs(gettext('Service Providers')));
var tableId = gui.providers.table({
rowSelect : 'multi',
rowSelect : 'single',
onEdit: function(value, event, table) {
gui.providers.rest.gui(value.type, {
success: function(data){
@ -105,12 +105,7 @@ gui.authenticators.link = function(event) {
gui.authenticators.rest.gui(value.type, {
success: function(data){
var form = gui.fields(data);
gui.appendToWorkspace(gui.modal('edit_modal', gettext('Edit authenticator'), form));
$('#edit_modal').modal()
.on('hidden.bs.modal', function () {
$('#edit_modal').remove();
})
;
gui.launchModal(gettext('Edit authenticator')+' '+value.name, form);
},
});
},
@ -152,6 +147,23 @@ gui.connectivity.link = function(event) {
rowSelect : 'multi',
container : 'transports-placeholder',
buttons : [ 'edit', 'delete', 'xls' ],
onEdit: function(value, event, table) {
gui.connectivity.transports.rest.gui(value.type, {
success: function(itemGui){
gui.connectivity.transports.rest.get({
id:value.id,
success: function(item) {
var form = gui.fields(itemGui, item);
gui.launchModal(gettext('Edit transport')+' '+value.name,form, function(form_selector) {
var fields = gui.fields.read(form_selector);
return false;
});
},
});
},
});
},
});
gui.connectivity.networks.table({
rowSelect : 'multi',

View File

@ -3,15 +3,17 @@
"use strict";
// Returns a form that will manage a gui description (new or edit)
gui.fields = function(item_description) {
var form = '<form class="form-horizontal" role="form">';
// item_description is expected to have fields sorted by .gui.order (REST api returns them sorted)
$.each(item_description, function(index, f){
gui.fields = function(itemGui, 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 + '">';
// itemGui is expected to have fields sorted by .gui.order (REST api returns them sorted)
$.each(itemGui, function(index, f){
gui.doLog(f);
var editing = false; // Locate real Editing
gui.doLog(item[f.name]);
form += api.templates.evaluate('tmpl_fld_'+f.gui.type, {
value: f.value || f.gui.value || f.gui.defvalue, // If no value present, use default value
value: item[f.name] || f.gui.value || f.gui.defvalue, // If no value present, use default value
values: f.gui.values,
label: f.gui.label,
length: f.gui.length,
@ -21,11 +23,29 @@
tooltip: f.gui.tooltip,
type: f.gui.type,
name: f.name,
css: 'modal_field_data',
});
});
form += '</form>';
return form;
};
// Reads fields from a form
gui.fields.read = function(formSelector) {
var res = {};
$(formSelector + ' .modal_field_data').each(function(i, field) {
var $field = $(field);
if( $field.attr('name') ) { // Is a valid field
if( $field.attr('type') == 'checkbox') {
res[$field.attr('name')] = $field.is(':checked');
} else {
res[$field.attr('name')] = $field.val();
}
}
});
gui.doLog(res);
return res;
};
}(window.gui = window.gui || {}, jQuery));

View File

@ -105,6 +105,36 @@
});
};
gui.launchModal = function(title, content, onSuccess) {
var id = Math.random().toString().split('.')[1];
gui.appendToWorkspace(gui.modal(id, title, content));
id = '#' + id; // for jQuery
// 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: 500, placement: 'auto right'});
// And catch "accept" (default is "Save" in fact) button click
$(id + ' .button-accept').click(function(){
if( onSuccess ) {
if( onSuccess(id + ' form') === false ) // Some error may have ocurred, do not close dialog
return;
}
$(id).modal('hide');
});
// Launch modal
$(id).modal()
.on('hidden.bs.modal', function () {
$(id).remove();
});
};
gui.clearWorkspace = function() {
$('#content').empty();
$('#minimized').empty();

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
<link href="{% get_static_prefix %}css/font-awesome.min.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/bootstrap-formhelpers.min.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/bootstrap-switch.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/bootstrap-select.min.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}adm/css/jquery.dataTables.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}adm/css/TableTools.css" rel="stylesheet" media="screen">
@ -33,29 +34,32 @@
{% block menu %}{% include 'uds/admin/snippets/navbar.html' %}{% endblock %}
<!-- End of menu -->
<!-- Content -->
<div id="page-wrapper">
<div id="content">
{% block body %}{% endblock %}
</div>
<div class="row">
<div id="minimized" class="col-lg-12">
</div>
</div>
<!-- Content -->
<div id="page-wrapper">
<div id="content">
{% block body %}{% endblock %}
</div>
<!-- End of content -->
</div>
</div>
<div class="row">
<div id="minimized" class="col-lg-12">
</div>
</div>
</div>
</div>
<script src="{% url 'uds.web.views.jsCatalog' LANGUAGE_CODE %}"></script>
<script src="{% get_static_prefix %}js/jquery-1.10.2.min.js"></script>
<script src="{% get_static_prefix %}js/jquery.cookie.js"></script>
<script src="{% get_static_prefix %}js/bootstrap.min.js"></script>
<script src="{% get_static_prefix %}js/bootstrap-switch.min.js"></script>
<script src="{% get_static_prefix %}js/bootstrap-select.min.js"></script>
<script src="{% get_static_prefix %}adm/js/jquery.blockUI.js"></script>
<script src="{% get_static_prefix %}adm/js/jquery.dataTables.min.js"></script>
<script src="{% get_static_prefix %}adm/js/TableTools.min.js"></script>
<!-- template engine -->
<script src="{% get_static_prefix %}adm/js/handlebars-v1.1.2.js"></script>
<!-- for "save" from javascript -->
<script src="{% get_static_prefix %}adm/js/Blob.js"></script>

View File

@ -1,19 +1,21 @@
{% load i18n html5 static %}
{% load i18n %}
<div class="row">
<div class="col-xs-12">
<h1>{% trans 'Authenticators' %} <small>{% trans 'administration of authenticators' %}</small></h1>
<h1>{% trans 'Authenticators' %}</h1>
<ol class="breadcrumb">
<li><a class="lnk-dashboard" href="#"><i class="fa fa-dashboard"></i> Dashboard</a></li>
<li>otra cosa</li>
</ol>
</div>
</div><!-- /.row -->
{% verbatim %}
<div class="row">
<div id="<%= auths %>" class="col-xs-12"></div>
<div id="{{ auths }}" class="col-xs-12"></div>
</div>
<div class="row">
<div id="<%= groups %>" class="col-xs-12"></div>
<div id="{{ groups }}" class="col-xs-12"></div>
</div>
<div class="row">
<div id="<%= users %>" class="col-xs-12"></div>
<div id="{{ users }}" class="col-xs-12"></div>
</div>
{% endverbatim %}

View File

@ -1,4 +1,4 @@
{% load i18n html5 static %}
{% load i18n %}
<div class="row">
<div class="col-xs-12">
<h1>{% trans 'Connectivity' %} <small>{% trans 'overview' %}</small></h1>
@ -7,10 +7,11 @@
</ol>
</div>
</div><!-- /.row -->
{% verbatim %}
<div class="row">
<div class="col-lg-6" id="<%= transports %>">
<div class="col-lg-6" id="{{ transports }}">
</div>
<div class="col-lg-6" id="<%= networks %>">
<div class="col-lg-6" id="{{ networks }}">
</div>
</div>
</div>
{% endverbatim %}

View File

@ -0,0 +1,9 @@
{% extends "uds/admin/tmpl/fld/form-group.html" %}
{% load i18n %}
{% block field %}
<div class="make-switch" data-on-label="{% trans 'Yes' %}" data-off-label="{% trans 'No' %}">
{% verbatim %}
<input type="checkbox" class="{{ css }}" name="{{ name }}" id="{{ name }}_field" {{# ifequals value true }}checked{{/ ifequals }}>
</div>
{% endverbatim %}
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends "uds/admin/tmpl/fld/form-group.html" %}
{% load i18n %}
{% block field %}
{% verbatim %}
<select class="selectpicker show-menu-arrow {{ css }}" name="{{ name }}" id="{{ name }}_field">
{{#each values }}
<option value="{{ id }}"{{# ifequals id ../value }}selected{{/ ifequals }}>{{ text }}</option>
{{/each}}
</select>
{% endverbatim %}
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends "uds/admin/tmpl/fld/form-group.html" %}
{% load i18n %}
{% verbatim %}
{% endverbatim %}

View File

@ -0,0 +1,9 @@
{% load i18n %}
{% verbatim %}
<div class="form-group">
<label for="{{ name }}_field" class="col-sm-3 control-label" data-toggle="tooltip" data-title="{{ tooltip }}">{{ label }}</label>
<div class="col-sm-9">
{% endverbatim %}
{% block field %}EMPTY!!!{% endblock %}
</div>
</div>

View File

@ -0,0 +1,3 @@
{% verbatim %}
<input type="hidden" class="{{ css }}" name="{{ name }}}" value="{{ value }}">
{% endverbatim %}

View File

@ -0,0 +1,5 @@
{% extends "uds/admin/tmpl/fld/form-group.html" %}
{% load i18n %}
{% verbatim %}
{% endverbatim %}

View File

@ -1,6 +1,7 @@
<div class="form-group">
<label for="<%= name %>_field" class="col-sm-3 control-label"><%= label %></label>
<div class="col-sm-9">
<input type="numeric" class="form-control" id="<%= name %>_field" placeholder="<%= tooltip %>">
</div>
</div>
{% extends "uds/admin/tmpl/fld/form-group.html" %}
{% load i18n %}
{% block field %}
{% verbatim %}
<input type="numeric" name="{{ name }}" class="form-control {{ css }}" id="{{ name }}_field" placeholder="{{ tooltip }}" value="{{ value }}">
{% endverbatim %}
{% endblock %}

View File

@ -0,0 +1,2 @@
{% verbatim %}
{% endverbatim %}

View File

@ -1,6 +1,7 @@
<div class="form-group">
<label for="<%= name %>_field" class="col-sm-3 control-label"><%= label %></label>
<div class="col-sm-9">
<input type="text" class="form-control" id="<%= name %>_field" placeholder="<%= tooltip %>">
</div>
</div>
{% extends "uds/admin/tmpl/fld/form-group.html" %}
{% load i18n %}
{% block field %}
{% verbatim %}
<input type="text" class="form-control {{ css }}" name="{{ name }}" id="{{ name }}_field" placeholder="{{ tooltip }}" value="{{ value }}">
{% endverbatim %}
{% endblock %}

View File

@ -0,0 +1,2 @@
{% verbatim %}
{% endverbatim %}

View File

@ -1,17 +1,30 @@
{% load i18n %}
<div id="<%= id %>" class="modal fade">
{% verbatim %}
<div id="{{ id }}" class="modal fade">
<div class="modal-dialog">
<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 %>
{{{ content }}}
{{# if button1 }}
{{{ button1 }}}
{{ else }}
{% endverbatim %}
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close' %}</button>
<button type="button" class="btn btn-primary">{% trans 'Save' %}</button>
{% verbatim %}
{{/ if }}
{{# if button2 }}
{{{ button1 }}}
{{ else }}
{% endverbatim %}
<button type="button" class="btn btn-primary button-accept">{% trans 'Save' %}</button>
{% verbatim %}
{{/ if }}
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endverbatim %}

View File

@ -1,3 +1,4 @@
{% verbatim %}
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook
@ -9,7 +10,7 @@
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Author>UDS Administration</Author>
<LastAuthor>UDS Administration Interface</LastAuthor>
<Created><%= creation_date %></Created>
<Created>{{ creation_date }}</Created>
<Company>UDS</Company>
<Version>1.0</Version>
</DocumentProperties>
@ -34,10 +35,10 @@
<Font x:Family="Verdana" ss:Bold="1" />
</Style>
</Styles>
<Worksheet ss:Name="<%= worksheet %>">
<Table ss:ExpandedColumnCount="<%= columns_count %>" ss:ExpandedRowCount="<%= rows_count %>"
<Worksheet ss:Name="{{ worksheet }}">
<Table ss:ExpandedColumnCount="{{ columns_count }}" ss:ExpandedRowCount="{{ rows_count }}"
x:FullColumns="1" x:FullRows="1">
<%= rows %>
{{{ rows }}}
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<Print>
@ -58,3 +59,4 @@
</WorksheetOptions>
</Worksheet>
</Workbook>
{% endverbatim %}

View File

@ -1,15 +1,17 @@
<div class="panel panel-primary" id="<%= panelId %>" data-minimized="<%= title %>">
{% verbatim %}
<div class="panel panel-primary" id="{{ panelId }}" data-minimized="{{ title }}">
<div class="panel-heading">
<h3 class="panel-title"><span class="fa fa-<%= icon %>"></span> <%= title %>
<h3 class="panel-title"><span class="fa fa-{{ icon }}"></span> {{ title }}
<span class="panel-icon fa fa-dot-circle-o pull-right"
onclick="gui.minimizePanel('#<%= panelId %>');"> </span>
onclick="gui.minimizePanel('#{{ panelId }}');"> </span>
<span class="panel-icon fa chevron pull-right" data-toggle="collapse"
data-target="#<%= panelId %> > div.panel-body"> </span>
data-target="#{{ panelId }} > div.panel-body"> </span>
<span class="panel-icon fa fa-refresh pull-right"> </span>
</h3>
</div>
<div class="panel-body collapse in">
<table class="table table-striped table-bordered table-hover" id="<%= table_id %>">
<table class="table table-striped table-bordered table-hover" id="{{ table_id }}">
</table>
</div>
</div>
{% endverbatim %}