src/intlconv.c \
src/profile.c \
src/bouquet.c \
- src/lock.c
+ src/lock.c \
+ src/wizard.c
SRCS = $(SRCS-1)
I18N-C = $(SRCS-1)
src/api/api_bouquet.c \
src/api/api_language.c \
src/api/api_satip.c \
- src/api/api_timeshift.c
+ src/api/api_timeshift.c \
+ src/api/api_wizard.c
SRCS-2 += \
src/parsers/parsers.c \
JAVASCRIPT += $(ROOTPATH)/app/config.js
JAVASCRIPT += $(ROOTPATH)/app/tvhlog.js
JAVASCRIPT += $(ROOTPATH)/app/status.js
+JAVASCRIPT += $(ROOTPATH)/app/wizard.js
JAVASCRIPT += $(ROOTPATH)/tv.js
JAVASCRIPT += $(ROOTPATH)/app/servicemapper.js
api_language_init();
api_satip_server_init();
api_timeshift_init();
+ api_wizard_init();
}
void api_done ( void )
void api_language_init ( void );
void api_satip_server_init ( void );
void api_timeshift_init ( void );
+void api_wizard_init ( void );
/*
* IDnode
--- /dev/null
+/*
+ * API - Wizard interface
+ *
+ * Copyright (C) 2015 Jaroslav Kysela
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; withm even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "channels.h"
+#include "access.h"
+#include "api.h"
+#include "config.h"
+#include "wizard.h"
+
+static int
+wizard_idnode_load_simple
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ int r;
+ wizard_build_fcn_t fcn = opaque;
+ wizard_page_t *page = fcn();
+ r = api_idnode_load_simple(perm, &page->idnode, op, args, resp);
+ page->free(page);
+ return r;
+}
+
+static int
+wizard_idnode_save_simple
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ int r;
+ wizard_build_fcn_t fcn = opaque;
+ wizard_page_t *page = fcn();
+ r = api_idnode_save_simple(perm, &page->idnode, op, args, resp);
+ page->free(page);
+ return r;
+}
+
+static int
+wizard_cancel
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ pthread_mutex_lock(&global_lock);
+ free(config.wizard);
+ config.wizard = strdup("");
+ config_save();
+ pthread_mutex_unlock(&global_lock);
+ return 0;
+}
+
+void
+api_wizard_init ( void )
+{
+ static api_hook_t ah[] = {
+ { "wizard/hello/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_hello },
+ { "wizard/hello/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_hello },
+ { "wizard/network/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_network },
+ { "wizard/network/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_network },
+ { "wizard/input/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_input },
+ { "wizard/input/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_input },
+ { "wizard/status/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_status },
+ { "wizard/status/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_status },
+ { "wizard/mapping/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_mapping },
+ { "wizard/mapping/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_mapping },
+ { "wizard/cancel", ACCESS_ADMIN, wizard_cancel, NULL },
+ { NULL },
+ };
+
+ api_register_all(ah);
+}
memset(&config, 0, sizeof(config));
config.idnode.in_class = &config_class;
+ config.wizard = strdup("hello");
config.info_area = strdup("login,storage,time");
config.cookie_expires = 7;
config.dscp = -1;
void config_done ( void )
{
/* note: tvhlog is inactive !!! */
+ free(config.wizard);
free(config.full_version);
free(config.server_name);
free(config.language);
.opts = PO_ADVANCED,
.group = 6,
},
+ {
+ .type = PT_STR,
+ .id = "wizard",
+ .name = "Wizard level", /* untranslated */
+ .off = offsetof(config_t, wizard),
+ .opts = PO_NOUI,
+ },
{}
}
};
uint32_t version;
int uilevel;
int uilevel_nochange;
+ char *wizard;
char *full_version;
char *server_name;
char *language;
const char *username = hc->hc_access ? (hc->hc_access->aa_username ?: "") : "";
int64_t bfree, btotal;
int dvr = !http_access_verify(hc, ACCESS_RECORDER);
+ int admin = !http_access_verify(hc, ACCESS_ADMIN);
const char *s;
htsmsg_add_str(m, "notificationClass", "accessUpdate");
if (hc->hc_peer_ipstr)
htsmsg_add_str(m, "address", hc->hc_peer_ipstr);
htsmsg_add_u32(m, "dvr", dvr);
- htsmsg_add_u32(m, "admin", !http_access_verify(hc, ACCESS_ADMIN));
+ htsmsg_add_u32(m, "admin", admin);
htsmsg_add_s64(m, "time", time(NULL));
htsmsg_add_s64(m, "totaldiskspace", btotal);
}
+ if (admin && config.wizard)
+ htsmsg_add_str(m, "wizard", config.wizard);
+
if(cmb->cmb_messages == NULL)
cmb->cmb_messages = htsmsg_create_list();
htsmsg_add_msg(cmb->cmb_messages, NULL, m);
background-image: url(../icons/find.png) !important;
}
+.wizard {
+ background-image: url(../icons/wand.png) !important;
+}
+
.uilevel {
background-image: url(../icons/application_form.png) !important;
}
background-image: url(../icons/fetch_images.png) !important;
}
+.previous {
+ background-image: url(../icons/arrow_left.png) !important;
+}
+
+.next {
+ background-image: url(../icons/arrow_right.png) !important;
+}
+
+
.x-linked {
display: inline-block;
background-image: url(../icons/linked.gif) !important;
var p = d[i];
if (conf.uuids && p.rdonly)
continue;
- if (conf.noui)
+ if (p.noui)
continue;
var f = tvheadend.idnode_editor_field(p, conf);
if (!f)
var cancelBtn = new Ext.Button({
text: _('Cancel'),
iconCls: 'cancel',
- handler: conf.cancel
+ handler: function() {
+ conf.cancel(conf);
+ }
});
buttons.push(cancelBtn);
}
var saveBtn = new Ext.Button({
- text: _('Save'),
- iconCls: 'save',
+ text: conf.saveText || _('Save'),
+ iconCls: conf.saveIconCls || 'save',
handler: function() {
if (panel.getForm().isDirty()) {
var node = panel.getForm().getFieldValues();
node.uuid = conf.uuids ? conf.uuids : item.uuid;
tvheadend.Ajax({
- url: 'api/idnode/save',
+ url: conf.saveURL || 'api/idnode/save',
params: {
node: Ext.encode(node)
},
if (conf.win)
conf.win.close();
}
+ if (conf.postsave)
+ conf.postsave(conf);
}
});
buttons.push(saveBtn);
var node = panel.getForm().getFieldValues();
node.uuid = conf.uuids ? conf.uuids : item.uuid;
tvheadend.Ajax({
- url: 'api/idnode/save',
+ url: conf.saveURL || 'api/idnode/save',
params: {
node: Ext.encode(node)
},
buttons.push(applyBtn);
}
+
var uilevelBtn = null;
- if (!tvheadend.uilevel_nochange && (!conf.uilevel || conf.uilevel !== 'expert')) {
+ if (!tvheadend.uilevel_nochange &&
+ (!conf.uilevel || conf.uilevel !== 'expert') &&
+ !conf.noUIlevel) {
uilevelBtn = tvheadend.idnode_uilevel_menu(uilevel, function(l) {
uilevel = l;
var values = panel.getForm().getFieldValues();
buttons.push(uilevelBtn ? '-' : '->');
buttons.push(helpBtn);
}
+
+ if (conf.buildbtn)
+ conf.buildbtn(conf, buttons);
}
panel = new Ext.form.FormPanel({
return panel;
};
+/*
+ *
+ */
+tvheadend.idnode_editor_win = function(_uilevel, item, conf)
+{
+ var p = tvheadend.idnode_editor(_uilevel, item, conf);
+ var width = p.fixedWidth;
+ var w = new Ext.ux.Window({
+ title: conf.winTitle,
+ iconCls: 'edit',
+ layout: 'fit',
+ autoWidth: width ? false : true,
+ autoHeight: true,
+ autoScroll: true,
+ plain: true,
+ items: p
+ });
+ conf.win = w;
+ if (width)
+ w.setWidth(width);
+ w.show();
+ return w;
+}
/*
* IDnode creation dialog
}
};
if (uuids.length > 1) {
- var title = String.format(_('Edit {0} ({1} entries)'),
- conf.titleS, uuids.length);
+ c.winTitle = String.format(_('Edit {0} ({1} entries)'),
+ conf.titleS, uuids.length);
c.uuids = uuids;
} else {
- var title = String.format(_('Edit {0}'), conf.titleS);
+ c.winTitle = String.format(_('Edit {0}'), conf.titleS);
}
- var p = tvheadend.idnode_editor(uilevel, d[0], c);
- var width = p.fixedWidth;
- w = new Ext.ux.Window({
- title: title,
- iconCls: 'edit',
- layout: 'fit',
- autoWidth: width ? false : true,
- autoHeight: true,
- autoScroll: true,
- plain: true,
- items: p
- });
- if (width)
- w.setWidth(width);
- c.win = w;
- w.show();
+ tvheadend.idnode_editor_win(uilevel, d[0], c);
}
});
}
tvheadend.dialog = null;
tvheadend.uilevel = 'expert';
tvheadend.uilevel_nochange = false;
+tvheadend.wizard = null;
tvheadend.cookieProvider = new Ext.state.CookieProvider({
// 7 days from now
}
tvheadend.rootTabPanel.doLayout();
+
+ if (o.wizard)
+ tvheadend.wizard_start(o.wizard);
}
/**
--- /dev/null
+/*
+ * Wizard
+ */
+
+tvheadend.wizard_start = function(page) {
+
+ var w = null;
+
+ function cancel(conf) {
+ tvheadend.Ajax({
+ url: 'api/wizard/cancel'
+ });
+ tvheadend.wizard = null;
+ if (conf.win)
+ conf.win.close();
+ }
+
+ function getparam(data, prefix) {
+ var m = data.params;
+ var l = prefix.length;
+ for (var i = 0; i < m.length; i++) {
+ var id = m[i].id;
+ if (id.substring(0, l) === prefix)
+ return id.substring(l, 32);
+ }
+ return null;
+ }
+
+ function newpage(conf, prefix) {
+ var p = getparam(conf.fullData, prefix);
+ if (p)
+ tvheadend.wizard_start(p);
+ else
+ Ext.MessageBox.alert(String.format(_('Wizard - page "{0}" not found'), prefix));
+ }
+
+ function buildbtn(conf, buttons) {
+ if (!getparam(conf.fullData, 'page_prev_'))
+ return;
+ var prevBtn = new Ext.Button({
+ text: _('Previous'),
+ iconCls: 'previous',
+ handler: function() {
+ newpage(conf, 'page_prev_');
+ },
+ });
+ buttons.splice(0, 0, prevBtn);
+ }
+
+ function build(d) {
+ d = json_decode(d);
+ var m = d[0];
+ var last = getparam(m, 'page_next_') === null;
+ tvheadend.idnode_editor_win('basic', m, {
+ fullData: m,
+ url: 'api/wizard/' + page,
+ winTitle: m.caption,
+ iconCls: 'wizard',
+ comet: m.events,
+ noApply: true,
+ noUIlevel: true,
+ postsave: function(conf) {
+ if (!last)
+ newpage(conf, 'page_next_');
+ else
+ cancel(conf);
+ },
+ buildbtn: buildbtn,
+ labelWidth: 250,
+ saveText: last ? _('Finish') : _('Save & Next'),
+ saveIconCls: last ? 'exit' : 'next',
+ cancel: cancel,
+ uilevel: 'expert',
+ help: function() {
+ new tvheadend.help(_('Wizard - initial configuration and tutorial'), 'config_wizard.html');
+ }
+ });
+ }
+
+ tvheadend.wizard = page;
+ tvheadend.Ajax({
+ url: 'api/wizard/' + page + '/load',
+ params: {
+ meta: 1
+ },
+ success: build,
+ failure: function(response, options) {
+ Ext.MessageBox.alert(_('Unable to obtain wizard page!'), response.statusText);
+ }
+ });
+
+};
--- /dev/null
+../../../../vendor/famfamsilk/arrow_left.png
\ No newline at end of file
--- /dev/null
+../../../../vendor/famfamsilk/arrow_right.png
\ No newline at end of file
--- /dev/null
+/*
+ * tvheadend, Wizard
+ * Copyright (C) 2015 Jaroslav Kysela
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "access.h"
+#include "settings.h"
+#include "wizard.h"
+
+/*
+ *
+ */
+
+static const void *empty_get(void *o)
+{
+ prop_sbuf[0] = '\0';
+ return &prop_sbuf_ptr;
+}
+
+#define SPECIAL_PROP(idval) { \
+ .type = PT_STR, \
+ .id = idval, \
+ .name = "", \
+ .get = empty_get, \
+ .opts = PO_RDONLY | PO_NOUI \
+}
+
+#define PREV_BUTTON(page) SPECIAL_PROP("page_prev_" STRINGIFY(page))
+#define NEXT_BUTTON(page) SPECIAL_PROP("page_next_" STRINGIFY(page))
+#define LAST_BUTTON() SPECIAL_PROP("page_last")
+
+/*
+ *
+ */
+
+static void page_free(wizard_page_t *page)
+{
+ free((char *)page->idnode.in_class);
+ free(page);
+}
+
+static wizard_page_t *page_init(const char *class_name, const char *caption)
+{
+ wizard_page_t *page = calloc(1, sizeof(*page));
+ idclass_t *ic = calloc(1, sizeof(*ic));
+ page->idnode.in_class = ic;
+ ic->ic_caption = caption;
+ ic->ic_class = ic->ic_event = class_name;
+ ic->ic_perm_def = ACCESS_ADMIN;
+ page->free = page_free;
+ return page;
+}
+
+/*
+ * Hello
+ */
+
+static const void *hello_get_network(void *o)
+{
+ strcpy(prop_sbuf, "Test123");
+ return &prop_sbuf_ptr;
+}
+
+static int hello_set_network(void *o, const void *v)
+{
+ return 0;
+}
+
+wizard_page_t *wizard_hello(void)
+{
+ static const property_t props[] = {
+ {
+ .type = PT_STR,
+ .id = "network",
+ .name = N_("Network (like 192.168.1.0/24)"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ {
+ .type = PT_STR,
+ .id = "username",
+ .name = N_("Username"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ {
+ .type = PT_STR,
+ .id = "password",
+ .name = N_("Password"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ NEXT_BUTTON(network),
+ {}
+ };
+ wizard_page_t *page = page_init("wizard_hello", N_("Welcome - Tvheadend - your TV streaming server and video recorder"));
+ idclass_t *ic = (idclass_t *)page->idnode.in_class;
+ ic->ic_properties = props;
+ return page;
+}
+
+/*
+ * Network settings
+ */
+
+wizard_page_t *wizard_network(void)
+{
+ static const property_t props[] = {
+ {
+ .type = PT_STR,
+ .id = "network1",
+ .name = N_("Network 1"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ {
+ .type = PT_STR,
+ .id = "network2",
+ .name = N_("Network 2"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ {
+ .type = PT_STR,
+ .id = "network3",
+ .name = N_("Network 3"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ PREV_BUTTON(hello),
+ NEXT_BUTTON(input),
+ {}
+ };
+ wizard_page_t *page = page_init("wizard_network", N_("Network settings"));
+ idclass_t *ic = (idclass_t *)page->idnode.in_class;
+ ic->ic_properties = props;
+ return page;
+}
+
+/*
+ * Input settings
+ */
+
+wizard_page_t *wizard_input(void)
+{
+ static const property_t props[] = {
+ {
+ .type = PT_STR,
+ .id = "input1",
+ .name = N_("Input 1 network"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ {
+ .type = PT_STR,
+ .id = "input2",
+ .name = N_("Input 2 network"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ {
+ .type = PT_STR,
+ .id = "input3",
+ .name = N_("Input 3 network"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ PREV_BUTTON(network),
+ NEXT_BUTTON(status),
+ {}
+ };
+ wizard_page_t *page = page_init("wizard_input", N_("Input / tuner settings"));
+ idclass_t *ic = (idclass_t *)page->idnode.in_class;
+ ic->ic_properties = props;
+ return page;
+}
+
+/*
+ * Status
+ */
+
+wizard_page_t *wizard_status(void)
+{
+ static const property_t props[] = {
+ {
+ .type = PT_STR,
+ .id = "muxes",
+ .name = N_("Found muxes"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ {
+ .type = PT_STR,
+ .id = "services",
+ .name = N_("Found services"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ PREV_BUTTON(input),
+ NEXT_BUTTON(mapping),
+ {}
+ };
+ wizard_page_t *page = page_init("wizard_status", N_("Scan status"));
+ idclass_t *ic = (idclass_t *)page->idnode.in_class;
+ ic->ic_properties = props;
+ return page;
+}
+
+/*
+ * Service Mapping
+ */
+
+wizard_page_t *wizard_mapping(void)
+{
+ static const property_t props[] = {
+ {
+ .type = PT_STR,
+ .id = "pnetwork",
+ .name = N_("Select network"),
+ .get = hello_get_network,
+ .set = hello_set_network,
+ },
+ PREV_BUTTON(status),
+ LAST_BUTTON(),
+ {}
+ };
+ wizard_page_t *page = page_init("wizard_service_map", N_("Service mapping"));
+ idclass_t *ic = (idclass_t *)page->idnode.in_class;
+ ic->ic_properties = props;
+ return page;
+}
--- /dev/null
+/*
+ * tvheadend, Wizard
+ * Copyright (C) 2015 Jaroslav Kysela
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TVH_WIZARD_H__
+#define __TVH_WIZARD_H__
+
+#include "tvheadend.h"
+#include "idnode.h"
+
+typedef struct wizard_page {
+ idnode_t idnode;
+ void (*free)(struct wizard_page *);
+} wizard_page_t;
+
+typedef wizard_page_t *(*wizard_build_fcn_t)(void);
+
+wizard_page_t *wizard_hello(void);
+wizard_page_t *wizard_network(void);
+wizard_page_t *wizard_input(void);
+wizard_page_t *wizard_status(void);
+wizard_page_t *wizard_mapping(void);
+
+#endif /* __TVH_WIZARD_H__ */