webui: more functionality for creating networks

This commit is contained in:
Adam Sutton 2013-06-04 16:56:56 +01:00
parent 07a677ecba
commit 75151722f6
3 changed files with 341 additions and 24 deletions

View file

@ -100,6 +100,7 @@ extjs_idnode_filter
return 1;
}
// TODO: move this
static htsmsg_t *
extjs_idnode_class ( const idclass_t *idc )
{
@ -112,7 +113,7 @@ extjs_idnode_class ( const idclass_t *idc )
}
static int
extjs_mpegts_services
extjs_mpegts_service
(http_connection_t *hc, const char *remain, void *opaque)
{
//char buf[256];
@ -166,7 +167,7 @@ extjs_mpegts_services
static int
extjs_mpegts_muxes
extjs_mpegts_mux
(http_connection_t *hc, const char *remain, void *opaque)
{
char buf[256];
@ -223,7 +224,7 @@ extjs_mpegts_muxes
}
static int
extjs_mpegts_networks
extjs_mpegts_network
(http_connection_t *hc, const char *remain, void *opaque)
{
mpegts_network_t *mn;
@ -278,6 +279,73 @@ extjs_mpegts_networks
return 0;
}
static int
extjs_mpegts_input
(http_connection_t *hc, const char *remain, void *opaque)
{
extern const idclass_t mpegts_input_class;
mpegts_input_t *mi;
mpegts_network_t *mn;
htsbuf_queue_t *hq = &hc->hc_reply;
const char *op = http_arg_get(&hc->hc_req_args, "op");
htsmsg_t *out = htsmsg_create_map();
extjs_grid_conf_t conf;
int total = 0;
if (!op) return 404;
if (!strcmp(op, "list")) {
htsmsg_t *list = htsmsg_create_list();
extjs_grid_conf(&hc->hc_req_args, &conf);
pthread_mutex_lock(&global_lock);
LIST_FOREACH(mi, &mpegts_input_all, mi_global_link) {
if (conf.filter && !extjs_idnode_filter(&mi->mi_id, conf.filter))
continue;
total++;
if (conf.start-- > 0)
continue;
if (conf.limit != 0) {
if (conf.limit > 0) conf.limit--;
htsmsg_t *e = htsmsg_create_map();
htsmsg_add_str(e, "uuid", idnode_uuid_as_str(&mi->mi_id));
idnode_save(&mi->mi_id, e);
htsmsg_add_msg(list, NULL, e);
}
}
pthread_mutex_unlock(&global_lock);
htsmsg_add_msg(out, "entries", list);
htsmsg_add_u32(out, "total", total);
} else if (!strcmp(op, "class")) {
htsmsg_t *list= extjs_idnode_class(&mpegts_input_class);
htsmsg_add_msg(out, "entries", list);
} else if (!strcmp(op, "network_class")) {
const char *uuid = http_arg_get(&hc->hc_req_args, "uuid");
if (!uuid) return 404;
mpegts_input_t *mi = idnode_find(uuid, &mpegts_input_class);
if (!mi) return 404;
htsmsg_t *list= extjs_idnode_class(mi->mi_network_class(mi));
htsmsg_add_msg(out, "entries", list);
} else if (!strcmp(op, "network_create")) {
const char *uuid = http_arg_get(&hc->hc_req_args, "uuid");
const char *conf = http_arg_get(&hc->hc_req_args, "conf");
if (!uuid || !conf) return 404;
mi = idnode_find(uuid, &mpegts_input_class);
if (!mi) return 404;
mn = mi->mi_network_create(mi, htsmsg_json_deserialize(conf));
if (mn)
mn->mn_config_save(mn);
else {
// TODO: Check for error
}
}
htsmsg_json_serialize(out, hq, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
htsmsg_destroy(out);
return 0;
}
/**
* DVB WEB user interface
@ -285,13 +353,14 @@ extjs_mpegts_networks
void
extjs_start_dvb(void)
{
printf("extjs_start_dvb()\n");
http_path_add("/api/mpegts/network",
NULL, extjs_mpegts_networks, ACCESS_WEB_INTERFACE);
NULL, extjs_mpegts_network, ACCESS_WEB_INTERFACE);
http_path_add("/api/mpegts/mux",
NULL, extjs_mpegts_muxes, ACCESS_WEB_INTERFACE);
NULL, extjs_mpegts_mux, ACCESS_WEB_INTERFACE);
http_path_add("/api/mpegts/service",
NULL, extjs_mpegts_services, ACCESS_WEB_INTERFACE);
NULL, extjs_mpegts_service, ACCESS_WEB_INTERFACE);
http_path_add("/api/mpegts/input",
NULL, extjs_mpegts_input, ACCESS_WEB_INTERFACE);
#if 0
http_path_add("/dvb/locations",
NULL, extjs_dvblocations, ACCESS_WEB_INTERFACE);

View file

@ -1,3 +1,146 @@
json_decode = function(d)
{
if (d && d.responseText) {
d = Ext.util.JSON.decode(d.responseText)
d = d.entries;
} else {
d = []
}
return d;
}
/*
* IDnode creation dialog
*/
tvheadend.idnode_create = function(conf)
{
var puuid = null;
var panel = null;
/* Buttons */
var saveBtn = new Ext.Button({
tooltip : 'Create new entry',
text : 'Create',
hidden : true,
handler : function(){
params = conf.create.params || {}
params['uuid'] = puuid;
params['conf'] = Ext.util.JSON.encode(panel.getForm().getValues());
Ext.Ajax.request({
url : conf.create.url || conf.url,
params : params,
success : function(d) {
win.close();
}
});
}
});
var undoBtn = new Ext.Button({
tooltip : 'Cancel operation',
text : 'Cancel',
handler : function(){
win.close();
}
});
/* Form */
panel = new Ext.FormPanel({
frame : true,
border : true,
bodyStyle : 'padding: 5px',
labelAlign : 'left',
labelWidth : 200,
autoWidth : true,
autoHeight : true,
defaultType : 'textfield',
buttonAlign : 'left',
items : [],
buttons : [ undoBtn, saveBtn ]
});
/* Create window */
win = new Ext.Window({
title : 'Add ' + conf.title,
layout : 'fit',
autoWidth : true,
autoHeight : true,
plain : true,
items : panel
});
/*
* Build the edit panel
*/
function build_form (d)
{
saveBtn.setVisible(true);
/* Fields */
for (i = 0; i < d.length; i++) {
if (d[i].rdonly) continue;
if (d[i].type == 'int' || d[i].type == 'u16' || d[i].type == 'u32') {
panel.add(new Ext.form.NumberField({
fieldLabel : d[i].caption,
name : d[i].id,
width : 300
}));
} else if (d[i].type == 'bool') {
panel.add(new Ext.form.Checkbox({
fieldLabel : d[i].caption,
name : d[i].id
}));
} else if (d[i].type == 'str') {
panel.add(new Ext.form.TextField({
fieldLabel : d[i].caption,
name : d[i].id,
width : 300
}));
}
}
panel.doLayout();
}
/* Do we need to first select a class? */
if (conf.select) {
/* Parent selector */
var combo = new Ext.form.ComboBox({
fieldLabel : conf.select.label,
grow : true,
editable : false,
allowBlank : false,
displayField : conf.select.displayField,
valueField : conf.select.valueField,
mode : 'remote',
triggerAction : 'all',
store : new Ext.data.JsonStore({
root : 'entries',
url : conf.select.url,
baseParams : conf.select.params,
fields : [ conf.select.valueField, conf.select.displayField ]
}),
listeners : {
select: function (s, n, o) {
params = conf.select.clazz.params || {};
params['uuid'] = puuid = n.data.uuid;
Ext.Ajax.request({
url : conf.select.clazz.url || conf.select.url || conf.url,
success : function(d) {
panel.remove(s);
build_form(json_decode(d));
},
params : params
});
}
}
});
panel.add(combo);
win.show();
}
}
/*
* IDnode grid
*/
@ -5,27 +148,42 @@ tvheadend.idnode_grid = function(panel, conf)
{
function build (d)
{
if (d && d.responseText) {
d = Ext.util.JSON.decode(d.responseText)
d = d.entries;
} else
d = []
/* Process object */
var columns = [];
var filters = [];
var fields = [];
var buttons = [];
var saveBtn = null;
var undoBtn = null;
var addBtn = null;
var delBtn = null;
/* Load Class */
d = json_decode(d);
/* Process */
for (i = 0; i < d.length; i++) {
var type = 'string';
if (d[i].type == 'int' || d[i].type == 'u16' || d[i].type == 'u32')
var edit = null;
if (d[i].type == 'int' || d[i].type == 'u16' || d[i].type == 'u32') {
type = 'numeric';
else if (d[i].type == 'bool')
if (!d[i].rdonly)
edit = new Ext.form.NumberField({
// TODO: min/max
})
} else if (d[i].type == 'bool') {
type = 'boolean';
if (!d[i].rdonly)
edit = new Ext.form.Checkbox({});
} else if (d[i].type == 'str') {
if (!d[i].rdonly)
edit = new Ext.form.TextField({});
}
fields.push(d[i].id)
columns.push({
dataIndex: d[i].id,
header : d[i].caption,
sortable : true
sortable : true,
editor : edit
});
filters.push({
type : type,
@ -59,19 +217,87 @@ tvheadend.idnode_grid = function(panel, conf)
columns : columns
});
/* Selection */
var select = new Ext.grid.RowSelectionModel({
singleSelect : false
});
/* Event handlers */
store.on('update', function(s, r, o){
d = (s.getModifiedRecords().length == 0);
undoBtn.setDisabled(d);
saveBtn.setDisabled(d);
});
select.on('selectionchange', function(s){
if (delBtn)
delBtn.setDisabled(s.getCount() == 0);
});
/* Top bar */
saveBtn = new Ext.Toolbar.Button({
tooltip : 'Save pending changes (marked with red border)',
iconCls : 'save',
text : 'Save',
disabled : true,
handler : function(){}
});
buttons.push(saveBtn);
undoBtn = new Ext.Toolbar.Button({
tooltip : 'Revert pending changes (marked with red border)',
iconCls : 'undo',
text : 'Undo',
disabled : true,
handler : function() {
store.rejectChanges();
}
});
buttons.push(undoBtn);
buttons.push('-');
if (conf.add) {
addBtn = new Ext.Toolbar.Button({
tooltip : 'Add a new entry',
iconCls : 'add',
text : 'Add',
disabled : false,
handler : function() {
tvheadend.idnode_create(conf.add);
}
});
buttons.push(addBtn);
}
if (conf.del) {
delBtn = new Ext.Toolbar.Button({
tooltip : 'Delete selected entries',
iconCls : 'remove',
text : 'Delete',
disabled : true,
handler : function(){}
});
buttons.push(delBtn);
}
buttons.push('->');
if (conf.help) {
buttons.push({
text : 'Help',
handler : conf.help
});
}
/* Grid Panel */
var grid = new Ext.grid.EditorGridPanel({
stripeRows : true,
title : conf.title,
store : store,
cm : model,
selModel : select,
plugins : [
filter
],
viewConfig : {
forceFit : true
},
bbar : new Ext.PagingToolbar({
tbar : buttons,
bbar : new Ext.PagingToolbar({
store : store,
pageSize : 50,
displayInfo : true,

View file

@ -9,23 +9,45 @@
tvheadend.networks = function(panel)
{
tvheadend.idnode_grid(panel, {
url : 'api/mpegts/network',
title: 'Networks'
title : 'Networks',
url : 'api/mpegts/network',
add : {
url : 'api/mpegts/input',
title : 'Network',
select : {
label : 'Input',
params : { op: 'list', limit: -1 },
displayField : 'fe_path',//displayname',
valueField : 'uuid',
url : 'api/mpegts/input',
clazz : {
params : { op: 'network_class' }
}
},
create : {
params : { op: 'network_create' }
}
},
del : true
});
}
tvheadend.muxes = function(panel)
{
tvheadend.idnode_grid(panel, {
url : 'api/mpegts/mux',
title: 'Muxes'
url : 'api/mpegts/mux',
title : 'Muxes',
add : true,
del : true
});
}
tvheadend.services = function(panel)
{
tvheadend.idnode_grid(panel, {
url : 'api/mpegts/service',
title: 'Services'
url : 'api/mpegts/service',
title : 'Services',
add : false,
del : false
});
}