webui: more functionality for creating networks
This commit is contained in:
parent
07a677ecba
commit
75151722f6
3 changed files with 341 additions and 24 deletions
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue