/* * tvheadend, AJAX / HTML user interface * Copyright (C) 2008 Andreas Öman * * 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 . */ #include #include #include #include #include #include #include "tvhead.h" #include "http.h" #include "ajaxui.h" #include "channels.h" /** * Render a channel group widget */ static void ajax_chgroup_build(htsbuf_queue_t *tq, channel_group_t *tcg) { htsbuf_qprintf(tq, "
  • ", tcg->tcg_tag); ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "", tcg->tcg_tag, tcg->tcg_name); if(tcg != defgroup) { htsbuf_qprintf(tq, "", tcg->tcg_tag, tcg->tcg_name); } htsbuf_qprintf(tq, "
    "); ajax_box_end(tq, AJAX_BOX_BORDER); htsbuf_qprintf(tq, "
  • "); } /** * Update order of channel groups */ static int ajax_chgroup_updateorder(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { channel_group_t *tcg; htsbuf_queue_t *tq = &hr->hr_q; http_arg_t *ra; TAILQ_FOREACH(ra, &hc->hc_req_args, link) { if(strcmp(ra->key, "channelgrouplist[]") || (tcg = channel_group_by_tag(atoi(ra->val))) == NULL) continue; TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link); TAILQ_INSERT_TAIL(&all_channel_groups, tcg, tcg_global_link); } channel_group_settings_write(); htsbuf_qprintf(tq, "Updated on server"); ajax_js(tq, "Effect.Fade('updatedok')"); http_output_html(hc, hr); return 0; } /** * Add a new channel group */ static int ajax_chgroup_add(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { channel_group_t *tcg; htsbuf_queue_t *tq = &hr->hr_q; const char *name; if((name = http_arg_get(&hc->hc_req_args, "name")) != NULL) { TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) if(!strcmp(name, tcg->tcg_name)) break; if(tcg == NULL) { tcg = channel_group_find(name, 1); ajax_chgroup_build(tq, tcg); /* We must recreate the Sortable object */ ajax_js(tq, "Sortable.destroy(\"channelgrouplist\")"); ajax_js(tq, "Sortable.create(\"channelgrouplist\", " "{onUpdate:function(){updatelistonserver(" "'channelgrouplist', " "'/ajax/chgroup_updateorder', " "'list-info'" ")}});"); } } http_output_html(hc, hr); return 0; } /** * Delete a channel group */ static int ajax_chgroup_del(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { channel_group_t *tcg; htsbuf_queue_t *tq = &hr->hr_q; const char *id; if((id = http_arg_get(&hc->hc_req_args, "id")) == NULL) return HTTP_STATUS_BAD_REQUEST; if((tcg = channel_group_by_tag(atoi(id))) == NULL) return HTTP_STATUS_BAD_REQUEST; htsbuf_qprintf(tq, "$('chgrp_%d').remove();", tcg->tcg_tag); http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0); channel_group_destroy(tcg); return 0; } /** * Channel group & channel configuration */ int ajax_config_channels_tab(http_connection_t *hc, http_reply_t *hr) { htsbuf_queue_t *tq = &hr->hr_q; channel_group_t *tcg; htsbuf_qprintf(tq, "
    "); ajax_box_begin(tq, AJAX_BOX_SIDEBOX, "channelgroups", NULL, "Channel groups"); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "
      "); TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) { if(tcg->tcg_hidden) continue; ajax_chgroup_build(tq, tcg); } htsbuf_qprintf(tq, "
    "); ajax_js(tq, "Sortable.create(\"channelgrouplist\", " "{onUpdate:function(){updatelistonserver(" "'channelgrouplist', " "'/ajax/chgroup_updateorder', " "'list-info'" ")}});"); /** * Add new group */ htsbuf_qprintf(tq, "
    "); ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL); htsbuf_qprintf(tq, "
    " "
    " "" "
    " "
    " "" "
    "); ajax_box_end(tq, AJAX_BOX_BORDER); ajax_box_end(tq, AJAX_BOX_SIDEBOX); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "
    "); http_output_html(hc, hr); return 0; } /** * Display all channels within the group */ static int ajax_chgroup_editor(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { htsbuf_queue_t *tq = &hr->hr_q; channel_t *ch; channel_group_t *tcg, *tcg2; th_transport_t *t; char buf[10]; int nsources; ajax_table_t ta; if(remain == NULL || (tcg = channel_group_by_tag(atoi(remain))) == NULL) return HTTP_STATUS_BAD_REQUEST; htsbuf_qprintf(tq, "\r\n"); ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, tcg->tcg_name); ajax_table_top(&ta, hc, tq, (const char *[]) {"Channelname", "Sources", "", NULL}, (int[]){8,2,1}); TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) { snprintf(buf, sizeof(buf), "%d", ch->ch_tag); ajax_table_row_start(&ta, buf); nsources = 0; LIST_FOREACH(t, &ch->ch_transports, tht_ch_link) nsources++; ajax_table_cell(&ta, NULL, "%s", ch->ch_tag, ch->ch_name); ajax_table_cell(&ta, NULL, "%d", nsources); ajax_table_cell_checkbox(&ta); } ajax_table_bottom(&ta); htsbuf_qprintf(tq, "
    \r\n"); htsbuf_qprintf(tq, "
    "); ajax_button(tq, "Select all", "select_all()"); ajax_button(tq, "Select none", "select_none()"); ajax_button(tq, "Invert selection", "select_invert()"); ajax_button(tq, "Select channels with sources", "select_sources()"); htsbuf_qprintf(tq, "
    \r\n"); htsbuf_qprintf(tq, "
    \r\n"); htsbuf_qprintf(tq, "
    "); ajax_button(tq, "Delete all selected...", "select_do('delete', '%d', 0, true);", tcg->tcg_tag); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, ""); htsbuf_qprintf(tq, ""); ajax_box_end(tq, AJAX_BOX_SIDEBOX); http_output_html(hc, hr); return 0; } /** * */ static struct strtab sourcetypetab[] = { { "DVB", TRANSPORT_DVB }, { "V4L", TRANSPORT_V4L }, { "IPTV", TRANSPORT_IPTV }, { "AVgen", TRANSPORT_AVGEN }, { "File", TRANSPORT_STREAMEDFILE }, }; static struct strtab cdlongname[] = { { "None", COMMERCIAL_DETECT_NONE }, { "Swedish TV4 Teletext", COMMERCIAL_DETECT_TTP192 }, }; /** * Display all channels within the group */ static int ajax_cheditor(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { htsbuf_queue_t *tq = &hr->hr_q; channel_t *ch, *ch2; channel_group_t *chg; th_transport_t *t; const char *s; int i; if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL) return HTTP_STATUS_BAD_REQUEST; ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, ch->ch_name); if(ch->ch_icon != NULL) { htsbuf_qprintf(tq, "
    " "
    ", ch->ch_icon); } htsbuf_qprintf(tq, "
    Sources:
    "); LIST_FOREACH(t, &ch->ch_transports, tht_ch_link) { ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "
    %s
    ", val2str(t->tht_type, sourcetypetab) ?: "???"); htsbuf_qprintf(tq, "
    \"%s\"%s
    ", t->tht_svcname, t->tht_scrambled ? " - (scrambled)" : ""); s = t->tht_sourcename ? t->tht_sourcename(t) : NULL; htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "
    " "" "
    ", t->tht_disabled ? "" : "checked ", t->tht_identifier); if(s != NULL) htsbuf_qprintf(tq, "
    %s
    ", s); htsbuf_qprintf(tq, "
    "); ajax_box_end(tq, AJAX_BOX_BORDER); } htsbuf_qprintf(tq, "
    \r\n"); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "", ch->ch_tag, ch->ch_name); htsbuf_qprintf(tq, "", ch->ch_tag, ch->ch_name); htsbuf_qprintf(tq, ""); htsbuf_qprintf(tq, "
    "); htsbuf_qprintf(tq, "
    \r\n"); htsbuf_qprintf(tq, "
    " "Commercial detection:
    " "
    " "
    "); htsbuf_qprintf(tq, ""); ajax_box_end(tq, AJAX_BOX_SIDEBOX); http_output_html(hc, hr); return 0; } /** * Change group for channel(s) */ static int ajax_changegroup(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { htsbuf_queue_t *tq = &hr->hr_q; channel_t *ch; channel_group_t *tcg; http_arg_t *ra; const char *s; const char *curgrp; if((s = http_arg_get(&hc->hc_req_args, "arg1")) == NULL) return HTTP_STATUS_BAD_REQUEST; if((curgrp = http_arg_get(&hc->hc_req_args, "arg2")) == NULL) return HTTP_STATUS_BAD_REQUEST; tcg = channel_group_by_tag(atoi(s)); if(tcg == NULL) return HTTP_STATUS_BAD_REQUEST; TAILQ_FOREACH(ra, &hc->hc_req_args, link) { if(strcmp(ra->val, "selected")) continue; if((ch = channel_by_tag(atoi(ra->key))) != NULL) channel_set_group(ch, tcg); } htsbuf_qprintf(tq, "$('cheditortab').innerHTML=''; " "new Ajax.Updater('groupeditortab', " "'/ajax/chgroup_editor/%s', " "{method: 'get', evalScripts: true});", curgrp); http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0); return 0; } /** * Change commercial detection type for channel(s) */ static int ajax_chsetcomdetect(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { channel_t *ch; const char *s; if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL) return HTTP_STATUS_BAD_REQUEST; if((s = http_arg_get(&hc->hc_req_args, "how")) == NULL) return HTTP_STATUS_BAD_REQUEST; ch->ch_commercial_detection = atoi(s); channel_settings_write(ch); http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0); return 0; } /** * Rename a channel */ static int ajax_chrename(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { htsbuf_queue_t *tq = &hr->hr_q; channel_t *ch; const char *s; if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL) return HTTP_STATUS_BAD_REQUEST; if((s = http_arg_get(&hc->hc_req_args, "newname")) == NULL) return HTTP_STATUS_BAD_REQUEST; if(channel_rename(ch, s)) { htsbuf_qprintf(tq, "alert('Channel already exist');"); } else { htsbuf_qprintf(tq, "new Ajax.Updater('groupeditortab', " "'/ajax/chgroup_editor/%d', " "{method: 'get', evalScripts: true});\r\n", ch->ch_group->tcg_tag); htsbuf_qprintf(tq, "new Ajax.Updater('cheditortab', " "'/ajax/cheditor/%d', " "{method: 'get', evalScripts: true});\r\n", ch->ch_tag); } http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0); return 0; } /** * Delete channel */ static int ajax_chdelete(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { htsbuf_queue_t *tq = &hr->hr_q; channel_t *ch; channel_group_t *tcg; if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL) return HTTP_STATUS_BAD_REQUEST; tcg = ch->ch_group; channel_delete(ch); htsbuf_qprintf(tq, "new Ajax.Updater('groupeditortab', " "'/ajax/chgroup_editor/%d', " "{method: 'get', evalScripts: true});\r\n", tcg->tcg_tag); htsbuf_qprintf(tq, "$('cheditortab').innerHTML='';\r\n"); http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0); return 0; } /** * Merge channel */ static int ajax_chmerge(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { htsbuf_queue_t *tq = &hr->hr_q; channel_t *src, *dst; channel_group_t *tcg; const char *s; if(remain == NULL || (src = channel_by_tag(atoi(remain))) == NULL) return HTTP_STATUS_NOT_FOUND; if((s = http_arg_get(&hc->hc_req_args, "dst")) == NULL) return HTTP_STATUS_BAD_REQUEST; if((dst = channel_by_tag(atoi(s))) == NULL) return HTTP_STATUS_BAD_REQUEST; tcg = src->ch_group; channel_merge(dst, src); htsbuf_qprintf(tq, "new Ajax.Updater('groupeditortab', " "'/ajax/chgroup_editor/%d', " "{method: 'get', evalScripts: true});\r\n", tcg->tcg_tag); htsbuf_qprintf(tq, "$('cheditortab').innerHTML='';\r\n"); http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0); return 0; } /** * Change group for channel(s) */ static int ajax_chdeletemulti(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { htsbuf_queue_t *tq = &hr->hr_q; channel_t *ch; http_arg_t *ra; const char *curgrp; if((curgrp = http_arg_get(&hc->hc_req_args, "arg1")) == NULL) return HTTP_STATUS_BAD_REQUEST; TAILQ_FOREACH(ra, &hc->hc_req_args, link) { if(strcmp(ra->val, "selected")) continue; if((ch = channel_by_tag(atoi(ra->key))) != NULL) channel_delete(ch); } htsbuf_qprintf(tq, "$('cheditortab').innerHTML=''; " "new Ajax.Updater('groupeditortab', " "'/ajax/chgroup_editor/%s', " "{method: 'get', evalScripts: true});", curgrp); http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0); return 0; } /** * */ void ajax_config_channels_init(void) { http_path_add("/ajax/chgroup_add" , NULL, ajax_chgroup_add, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chgroup_del" , NULL, ajax_chgroup_del, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chgroup_updateorder", NULL, ajax_chgroup_updateorder, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chgroup_editor", NULL, ajax_chgroup_editor, AJAX_ACCESS_CONFIG); http_path_add("/ajax/cheditor", NULL, ajax_cheditor, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chop/changegroup", NULL, ajax_changegroup, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chsetcomdetect", NULL, ajax_chsetcomdetect, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chrename", NULL, ajax_chrename, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chdelete", NULL, ajax_chdelete, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chmerge", NULL, ajax_chmerge, AJAX_ACCESS_CONFIG); http_path_add("/ajax/chop/delete", NULL, ajax_chdeletemulti, AJAX_ACCESS_CONFIG); }