From 8a2eefa22318e5c46ec7c9a1e0f11cdd12c82778 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Apr 2026 20:15:58 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=95=20Sync=202026-04-25=2020:15:58?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3ginfo/Makefile | 4 +- .../usr/share/3ginfo/html/3ginfo.js | 11 +- luci-app-bandix/Makefile | 4 +- .../resources/view/bandix/connection.js | 123 ++++++++++++++++-- .../resources/view/bandix/index.js | 117 ++++++++++++++++- luci-app-bandix/po/es/bandix.po | 3 + luci-app-bandix/po/fr/bandix.po | 3 + luci-app-bandix/po/ja/bandix.po | 3 + luci-app-bandix/po/pl/bandix.po | 3 + luci-app-bandix/po/ru/bandix.po | 3 + luci-app-bandix/po/zh_Hans/bandix.po | 3 + luci-app-bandix/po/zh_Hant/bandix.po | 3 + .../root/usr/libexec/rpcd/luci.bandix | 16 ++- luci-base/ucode/template/header.ut | 22 ++++ .../resources/view/network/wireless.js | 93 +++++++++++-- openwrt-bandix/Makefile | 6 +- teleproxy/Makefile | 4 +- teleproxy/files/teleproxy-config-refresh.sh | 48 +++++++ teleproxy/files/teleproxy.init | 8 +- 19 files changed, 438 insertions(+), 39 deletions(-) create mode 100755 teleproxy/files/teleproxy-config-refresh.sh diff --git a/3ginfo/Makefile b/3ginfo/Makefile index 612b7c9b..f8afe67b 100644 --- a/3ginfo/Makefile +++ b/3ginfo/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=3ginfo -PKG_VERSION:=20260404 -PKG_RELEASE:=2 +PKG_VERSION:=20260425 +PKG_RELEASE:=3 include $(INCLUDE_DIR)/package.mk diff --git a/3ginfo/files-html/usr/share/3ginfo/html/3ginfo.js b/3ginfo/files-html/usr/share/3ginfo/html/3ginfo.js index 55151530..162380b3 100644 --- a/3ginfo/files-html/usr/share/3ginfo/html/3ginfo.js +++ b/3ginfo/files-html/usr/share/3ginfo/html/3ginfo.js @@ -365,7 +365,16 @@ function showmodemparams() { } if (data.cid_dec && data.cid_dec > 0 && data.operator_mcc == 260) { - document.getElementById('btsearch' + idx).setAttribute("href", "https://btsearch.pl/stations?q=" + data.cid_dec); + switch (data.operator_mnc) { + case '01': + case '02': + case '03': + case '06': + document.getElementById('btsearch' + idx).setAttribute('href', 'https://btsearch.pl/stations?mnc=260' + data.operator_mnc + '&q=' + data.cid_dec); + break; + default: + document.getElementById('btsearch' + idx).setAttribute('href', 'https://btsearch.pl/stations?q=' + data.cid_dec); + } setDisplay('div_btsearch' + idx, true); } else { setDisplay('div_btsearch' + idx, false); diff --git a/luci-app-bandix/Makefile b/luci-app-bandix/Makefile index 88f2aef2..c7b706bc 100644 --- a/luci-app-bandix/Makefile +++ b/luci-app-bandix/Makefile @@ -10,8 +10,8 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix PKG_MAINTAINER:=timsaya -PKG_VERSION:=0.12.6 -PKG_RELEASE:=4 +PKG_VERSION:=0.12.7 +PKG_RELEASE:=5 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js index 1478cad5..8aa3df5c 100644 --- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js +++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js @@ -150,7 +150,7 @@ var callGetConnection = rpc.declare({ var callGetConnectionFlows = rpc.declare({ object: 'luci.bandix', method: 'getConnectionFlows', - params: ['ip', 'protocol', 'state'], + params: ['ip', 'protocol', 'state', 'page', 'page_size'], expect: {} }); @@ -688,14 +688,16 @@ return view.extend({ flex-shrink: 0; } - .flows-modal-content .flows-filters select { + .flows-modal-content .flows-filters select, + .flows-modal-content .flows-footer select { padding: 6px 10px; border-radius: 4px; border: 1px solid rgba(0,0,0,0.2); font-size: 0.875rem; } - .flows-modal-content.theme-dark .flows-filters select { + .flows-modal-content.theme-dark .flows-filters select, + .flows-modal-content.theme-dark .flows-footer select { background-color: #3a3a3a; border-color: rgba(255,255,255,0.2); color: #e5e5e5; @@ -778,6 +780,13 @@ return view.extend({ flex-shrink: 0; display: flex; justify-content: flex-end; + align-items: center; + gap: 8px; + flex-wrap: wrap; + } + + .flows-modal-content .flows-footer .flows-page-size-select { + min-width: 92px; } .flows-modal-content.theme-dark .flows-footer { @@ -1051,6 +1060,11 @@ return view.extend({ function showFlowsModal(deviceIp, deviceInfo) { var currentTheme = getThemeMode(); + var currentPage = 1; + var totalPages = 1; + var totalItems = 0; + var currentPageSize = 100; + var protocolSelect = E('select', { 'class': 'cbi-input-select', 'id': 'flows-protocol' }, [ E('option', { 'value': '' }, _('All')), E('option', { 'value': 'tcp' }, 'TCP'), @@ -1068,6 +1082,15 @@ return view.extend({ E('option', { 'value': 'LAST_ACK' }, 'LAST_ACK'), E('option', { 'value': 'CLOSE' }, 'CLOSE') ]); + var pageSizeSelect = E('select', { 'class': 'cbi-input-select flows-page-size-select', 'id': 'flows-page-size' }, [ + E('option', { 'value': '50' }, '50'), + E('option', { 'value': '100', 'selected': 'selected' }, '100'), + E('option', { 'value': '200' }, '200'), + E('option', { 'value': '500' }, '500') + ]); + var pageInfo = E('span', { 'style': 'font-size: 0.85rem; opacity: 0.85; margin: 0 14px;' }, ''); + var prevPageBtn = E('button', { 'class': 'cbi-button cbi-button-action', 'style': 'margin-right: 8px;' }, _('Previous')); + var nextPageBtn = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Next')); var tableBody = E('tbody', { 'class': 'flows-table-body' }); var tableWrap = E('div', { 'class': 'flows-table-wrap' }, [ E('table', { 'class': 'flows-table' }, [ @@ -1088,22 +1111,75 @@ return view.extend({ ]) ]); - function loadFlows() { + function updatePaginationUi() { + if (pageInfo) { + pageInfo.textContent = _('Page') + ' ' + currentPage + '/' + totalPages + ' · ' + _('Total') + ': ' + totalItems; + } + if (prevPageBtn) { + prevPageBtn.disabled = currentPage <= 1; + } + if (nextPageBtn) { + nextPageBtn.disabled = currentPage >= totalPages; + } + } + + function normalizeFlowsResponse(res) { + if (!res || res.status !== 'success') { + return { items: [], total: 0, page: currentPage, page_size: currentPageSize, total_pages: 1 }; + } + + if (Array.isArray(res.data)) { + // 兼容旧后端:data 直接是数组 + return { + items: res.data, + total: res.data.length, + page: 1, + page_size: res.data.length || currentPageSize, + total_pages: 1 + }; + } + + if (res.data && Array.isArray(res.data.items)) { + // 新后端:分页对象 + return { + items: res.data.items, + total: res.data.total || 0, + page: res.data.page || currentPage, + page_size: res.data.page_size || currentPageSize, + total_pages: res.data.total_pages || 1 + }; + } + + return { items: [], total: 0, page: currentPage, page_size: currentPageSize, total_pages: 1 }; + } + + function loadFlows(resetPage) { + if (resetPage) { + currentPage = 1; + } var protocol = protocolSelect.value || ''; var state = stateSelect.value || ''; + currentPageSize = parseInt(pageSizeSelect.value, 10) || 100; tableBody.innerHTML = ''; tableBody.appendChild(E('tr', {}, [ E('td', { 'colspan': 9, 'class': 'loading-state' }, _('Loading…')) ])); - return callGetConnectionFlows(deviceIp, protocol || null, state || null).then(function (res) { + return callGetConnectionFlows(deviceIp, protocol || null, state || null, currentPage, currentPageSize).then(function (res) { tableBody.innerHTML = ''; - if (!res || res.status !== 'success' || !res.data || res.data.length === 0) { + var normalized = normalizeFlowsResponse(res); + var flows = normalized.items; + totalItems = normalized.total; + currentPage = normalized.page; + totalPages = Math.max(1, normalized.total_pages); + updatePaginationUi(); + + if (!flows || flows.length === 0) { tableBody.appendChild(E('tr', {}, [ E('td', { 'colspan': 9, 'class': 'loading-state' }, _('No connections')) ])); return; } - res.data.forEach(function (f) { + flows.forEach(function (f) { var stateCls = ''; if (f.state) { if (f.state.indexOf('ESTABLISHED') >= 0) stateCls = 'flows-state-est'; @@ -1131,15 +1207,19 @@ return view.extend({ ])); }); }).catch(function (err) { + totalItems = 0; + totalPages = 1; + currentPage = 1; + updatePaginationUi(); tableBody.innerHTML = ''; tableBody.appendChild(E('tr', {}, [ - E('td', { 'colspan': 9, 'class': 'error-state' }, _('Failed to load') + ': ' + (err.message || err)) + E('td', { 'colspan': 9, 'class': 'error-state' }, _('Failed to load') + ': ' + (err.message || err) + ' · ' + _('Try using filters or smaller page size')) ])); }); } function onFilterChange() { - loadFlows(); + loadFlows(true); } function hideFlowsModal() { @@ -1147,6 +1227,22 @@ return view.extend({ if (overlay) overlay.classList.remove('show'); } + prevPageBtn.addEventListener('click', function () { + if (currentPage > 1) { + currentPage -= 1; + loadFlows(false); + } + }); + nextPageBtn.addEventListener('click', function () { + if (currentPage < totalPages) { + currentPage += 1; + loadFlows(false); + } + }); + pageSizeSelect.addEventListener('change', function () { + loadFlows(true); + }); + var modalContent = E('div', { 'class': 'flows-modal-content theme-' + currentTheme }, [ E('div', { 'class': 'flows-modal-header' }, [ E('div', { 'class': 'flows-modal-title' }, _('Connection Details') + ' - ' + (deviceInfo.host || deviceInfo.ip4 || deviceIp)), @@ -1161,6 +1257,10 @@ return view.extend({ ]), tableWrap, E('div', { 'class': 'flows-footer' }, [ + pageSizeSelect, + prevPageBtn, + nextPageBtn, + pageInfo, E('button', { 'class': 'cbi-button cbi-button-reset', 'click': hideFlowsModal }, _('Close')) ]) ]); @@ -1181,7 +1281,8 @@ return view.extend({ overlay.appendChild(modalContent); overlay.classList.add('show'); modalContent.addEventListener('click', function (e) { e.stopPropagation(); }); - loadFlows(); + updatePaginationUi(); + loadFlows(true); } container.addEventListener('click', function (ev) { @@ -1260,4 +1361,4 @@ return view.extend({ return container; } -}); \ No newline at end of file +}); diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js index 948283fe..251b0855 100644 --- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js +++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/index.js @@ -6535,6 +6535,99 @@ return view.extend({ return increments.map(normalizeTrafficIncrementItem).filter(function (x) { return x; }); } + // 为了规避 ubus 1MB 响应限制,长时间范围按块分段请求 + var TRAFFIC_INCREMENT_CHUNK_MS_HOURLY = 14 * 24 * 60 * 60 * 1000; // 14 天 + var TRAFFIC_INCREMENT_CHUNK_MS_DAILY = 60 * 24 * 60 * 60 * 1000; // 60 天 + + function mergeIncrementsResponse(base, part) { + if (!part) return base; + if (Array.isArray(part.inc)) { + base.inc = base.inc.concat(part.inc); + } + + if (part.start && (!base.start || part.start < base.start)) { + base.start = part.start; + } + if (part.end && (!base.end || part.end > base.end)) { + base.end = part.end; + } + + base.t_rx_b = (base.t_rx_b || 0) + (part.t_rx_b || 0); + base.t_tx_b = (base.t_tx_b || 0) + (part.t_tx_b || 0); + base.t_b = (base.t_b || 0) + (part.t_b || 0); + + if (!base.agg && part.agg) base.agg = part.agg; + if (!base.mac && part.mac) base.mac = part.mac; + if (!base.net && part.net) base.net = part.net; + if (!base.network_type && part.network_type) base.network_type = part.network_type; + + return base; + } + + function buildTrafficIncrementsChunks(startMs, endMs, aggregation) { + if (!startMs || !endMs || endMs <= startMs) { + return null; + } + + var chunkMs = aggregation === 'daily' ? TRAFFIC_INCREMENT_CHUNK_MS_DAILY : TRAFFIC_INCREMENT_CHUNK_MS_HOURLY; + var totalRange = endMs - startMs; + if (totalRange <= chunkMs) { + return null; + } + + var chunks = []; + for (var s = startMs; s < endMs; s += chunkMs) { + chunks.push({ start: s, end: Math.min(s + chunkMs, endMs) }); + } + return chunks; + } + + function fetchTrafficIncrementsChunked(startMs, endMs, aggregation, mac, networkType, onProgress) { + var chunks = buildTrafficIncrementsChunks(startMs, endMs, aggregation); + if (!chunks) { + return callGetTrafficUsageIncrements(startMs, endMs, aggregation, mac, networkType); + } + + if (typeof onProgress === 'function') { + onProgress(0, chunks.length); + } + + var merged = { + start: startMs, + end: endMs, + agg: aggregation, + inc: [], + t_rx_b: 0, + t_tx_b: 0, + t_b: 0 + }; + + var chain = Promise.resolve(merged); + var finished = 0; + chunks.forEach(function (chunk) { + chain = chain.then(function (acc) { + return callGetTrafficUsageIncrements(chunk.start, chunk.end, aggregation, mac, networkType).then(function (part) { + finished += 1; + if (typeof onProgress === 'function') { + onProgress(finished, chunks.length); + } + return mergeIncrementsResponse(acc, part || {}); + }); + }); + }); + + return chain.then(function (result) { + if (Array.isArray(result.inc)) { + result.inc.sort(function (a, b) { + var aTs = (a && (a.start || a.ts_ms || a.end)) || 0; + var bTs = (b && (b.start || b.ts_ms || b.end)) || 0; + return aTs - bTs; + }); + } + return result; + }); + } + // 更新时间序列增量数据(使用 Traffic Timeline 自己的时间范围) // Traffic Timeline 缩放变量 var incrementsZoomEnabled = false; @@ -6568,7 +6661,29 @@ return view.extend({ selectedNetworkType = null; } - callGetTrafficUsageIncrements(startMs, endMs, selectedAggregation, selectedMac, selectedNetworkType).then(function (result) { + var container = document.getElementById('traffic-increments-container'); + var chunks = buildTrafficIncrementsChunks(startMs, endMs, selectedAggregation); + if (container) { + if (chunks && chunks.length > 1) { + container.innerHTML = '
' + _('Loading in chunks, please wait... (%d/%d)').format(0, chunks.length) + '
'; + } else { + container.innerHTML = '
' + _('Loading...') + '
'; + } + } + + fetchTrafficIncrementsChunked( + startMs, + endMs, + selectedAggregation, + selectedMac, + selectedNetworkType, + function (done, total) { + var progressContainer = document.getElementById('traffic-increments-container'); + if (progressContainer && total > 1) { + progressContainer.innerHTML = '
' + _('Loading in chunks, please wait... (%d/%d)').format(done, total) + '
'; + } + } + ).then(function (result) { if (!result || !result.inc) { var container = document.getElementById('traffic-increments-container'); if (container) { diff --git a/luci-app-bandix/po/es/bandix.po b/luci-app-bandix/po/es/bandix.po index 8ae3df77..65fd54e8 100644 --- a/luci-app-bandix/po/es/bandix.po +++ b/luci-app-bandix/po/es/bandix.po @@ -860,6 +860,9 @@ msgstr "Aún no hay reglas programadas, haga clic en \"Agregar regla\" para come msgid "Loading..." msgstr "Cargando..." +msgid "Loading in chunks, please wait... (%d/%d)" +msgstr "Carga por fragmentos, espere... (%d/%d)" + msgid "No schedule rules" msgstr "No hay reglas programadas" diff --git a/luci-app-bandix/po/fr/bandix.po b/luci-app-bandix/po/fr/bandix.po index 6db94280..2191e16e 100644 --- a/luci-app-bandix/po/fr/bandix.po +++ b/luci-app-bandix/po/fr/bandix.po @@ -860,6 +860,9 @@ msgstr "Aucune règle programmée pour le moment, cliquez sur \"Ajouter une règ msgid "Loading..." msgstr "Chargement..." +msgid "Loading in chunks, please wait... (%d/%d)" +msgstr "Chargement par morceaux, veuillez patienter... (%d/%d)" + msgid "No schedule rules" msgstr "Aucune règle programmée" diff --git a/luci-app-bandix/po/ja/bandix.po b/luci-app-bandix/po/ja/bandix.po index 6988e34c..2875a19d 100644 --- a/luci-app-bandix/po/ja/bandix.po +++ b/luci-app-bandix/po/ja/bandix.po @@ -860,6 +860,9 @@ msgstr "スケジュール制限ルールがまだありません。「ルール msgid "Loading..." msgstr "読み込み中..." +msgid "Loading in chunks, please wait... (%d/%d)" +msgstr "分割読み込み中です。しばらくお待ちください... (%d/%d)" + msgid "No schedule rules" msgstr "スケジュール制限ルールなし" diff --git a/luci-app-bandix/po/pl/bandix.po b/luci-app-bandix/po/pl/bandix.po index 4b05ba54..3c2cec83 100644 --- a/luci-app-bandix/po/pl/bandix.po +++ b/luci-app-bandix/po/pl/bandix.po @@ -860,6 +860,9 @@ msgstr "Brak jeszcze reguł limitu czasu, kliknij \"Dodaj regułę\", aby rozpoc msgid "Loading..." msgstr "Ładowanie..." +msgid "Loading in chunks, please wait... (%d/%d)" +msgstr "Ładowanie fragmentami, proszę czekać... (%d/%d)" + msgid "No schedule rules" msgstr "Brak reguł limitu czasu" diff --git a/luci-app-bandix/po/ru/bandix.po b/luci-app-bandix/po/ru/bandix.po index d8897d0b..ebdd27f5 100644 --- a/luci-app-bandix/po/ru/bandix.po +++ b/luci-app-bandix/po/ru/bandix.po @@ -860,6 +860,9 @@ msgstr "Правил расписания пока нет, нажмите \"До msgid "Loading..." msgstr "Загрузка..." +msgid "Loading in chunks, please wait... (%d/%d)" +msgstr "Поштучная загрузка, подождите... (%d/%d)" + msgid "No schedule rules" msgstr "Нет правил расписания" diff --git a/luci-app-bandix/po/zh_Hans/bandix.po b/luci-app-bandix/po/zh_Hans/bandix.po index ab064ace..a72d41fa 100644 --- a/luci-app-bandix/po/zh_Hans/bandix.po +++ b/luci-app-bandix/po/zh_Hans/bandix.po @@ -857,6 +857,9 @@ msgstr "还没有定时限速规则,点击"添加规则"开始设置" msgid "Loading..." msgstr "加载中..." +msgid "Loading in chunks, please wait... (%d/%d)" +msgstr "分段加载中,请稍等... (%d/%d)" + msgid "No schedule rules" msgstr "没有定时限速规则" diff --git a/luci-app-bandix/po/zh_Hant/bandix.po b/luci-app-bandix/po/zh_Hant/bandix.po index e342720b..5c114f97 100644 --- a/luci-app-bandix/po/zh_Hant/bandix.po +++ b/luci-app-bandix/po/zh_Hant/bandix.po @@ -860,6 +860,9 @@ msgstr "還沒有定時限速規則,點擊"新增規則"開始設定" msgid "Loading..." msgstr "載入中..." +msgid "Loading in chunks, please wait... (%d/%d)" +msgstr "分段載入中,請稍候... (%d/%d)" + msgid "No schedule rules" msgstr "沒有定時限速規則" diff --git a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix index 4e21e368..d4bdec52 100755 --- a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix +++ b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix @@ -346,11 +346,15 @@ get_connection_flows() { local ip="$1" local protocol="$2" local state="$3" + local page="$4" + local page_size="$5" local query_params="" [ -n "$ip" ] && query_params="${query_params}ip=$(printf '%s' "$ip" | sed 's/ /%20/g')&" [ -n "$protocol" ] && query_params="${query_params}protocol=$(printf '%s' "$protocol" | sed 's/ /%20/g')&" [ -n "$state" ] && query_params="${query_params}state=$(printf '%s' "$state" | sed 's/ /%20/g')&" + [ -n "$page" ] && query_params="${query_params}page=$page&" + [ -n "$page_size" ] && query_params="${query_params}page_size=$page_size&" query_params=$(echo "$query_params" | sed 's/&$//') local url="$BANDIX_CONNECTION_FLOWS_API" @@ -1248,6 +1252,8 @@ case "$1" in json_add_string "ip" json_add_string "protocol" json_add_string "state" + json_add_int "page" + json_add_int "page_size" json_close_object json_add_object "getDnsQueries" @@ -1444,6 +1450,8 @@ case "$1" in ip="" protocol="" state="" + page="" + page_size="" input="" if read -t 1 -r input; then : @@ -1455,12 +1463,18 @@ case "$1" in [ -z "$protocol" ] && protocol="$(echo "$input" | jsonfilter -e '$.protocol' 2>/dev/null)" state="$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)" [ -z "$state" ] && state="$(echo "$input" | jsonfilter -e '$.state' 2>/dev/null)" + page="$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)" + [ -z "$page" ] && page="$(echo "$input" | jsonfilter -e '$.page' 2>/dev/null)" + page_size="$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)" + [ -z "$page_size" ] && page_size="$(echo "$input" | jsonfilter -e '$.page_size' 2>/dev/null)" else [ -n "$3" ] && ip="$3" [ -n "$4" ] && protocol="$4" [ -n "$5" ] && state="$5" + [ -n "$6" ] && page="$6" + [ -n "$7" ] && page_size="$7" fi - get_connection_flows "$ip" "$protocol" "$state" + get_connection_flows "$ip" "$protocol" "$state" "$page" "$page_size" ;; getDnsQueries) # logger "luci.bandix: getDnsQueries called" diff --git a/luci-base/ucode/template/header.ut b/luci-base/ucode/template/header.ut index f23e5e45..e0f45885 100644 --- a/luci-base/ucode/template/header.ut +++ b/luci-base/ucode/template/header.ut @@ -4,9 +4,31 @@ -#} {% + import { access } from 'fs'; + import { cursor } from 'uci'; + + const wizard_request = ctx.request_path ?? []; + let wizard_pending = false; + + if (ctx.authsession && + wizard_request[0] == 'admin' && + wizard_request[1] != 'wizard' && + access('/etc/config/wizard') && + !access('/usr/sbin/quickstart') && + !access('/etc/config/finished')) { + let wizard_uci = cursor(); + wizard_pending = (wizard_uci.get('wizard', 'config', 'finished') != '1'); + } + include(`themes/${theme}/header`); -%} +{% if (wizard_pending): %} + +{% endif %} +