diff options
Diffstat (limited to 'applications')
83 files changed, 4438 insertions, 954 deletions
diff --git a/applications/luci-app-aria2/po/sv/aria2.po b/applications/luci-app-aria2/po/sv/aria2.po index a7f41f250d..3a129364cf 100644 --- a/applications/luci-app-aria2/po/sv/aria2.po +++ b/applications/luci-app-aria2/po/sv/aria2.po @@ -1,5 +1,5 @@ msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" +msgstr "Content-Type: text/plain; charset=UTF-8\n" msgid "\"Falloc\" is not available in all cases." msgstr "" diff --git a/applications/luci-app-clamav/po/sv/clamav.po b/applications/luci-app-clamav/po/sv/clamav.po index 589d5f9aa3..37de249e54 100644 --- a/applications/luci-app-clamav/po/sv/clamav.po +++ b/applications/luci-app-clamav/po/sv/clamav.po @@ -1,5 +1,5 @@ msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" +msgstr "Content-Type: text/plain; charset=UTF-8\n" msgid "10" msgstr "10" diff --git a/applications/luci-app-commands/po/sv/commands.po b/applications/luci-app-commands/po/sv/commands.po index 8cb1923e29..a944fdb63d 100644 --- a/applications/luci-app-commands/po/sv/commands.po +++ b/applications/luci-app-commands/po/sv/commands.po @@ -104,8 +104,8 @@ msgid "" "This page allows you to configure custom shell commands which can be easily " "invoked from the web interface." msgstr "" -"Den här sidan tillåter dig att ställa in anpassade skalkommandon som lättast kan " -"åberopas från webbgränssnittet." +"Den här sidan tillåter dig att ställa in anpassade skalkommandon som lättast " +"kan åberopas från webbgränssnittet." msgid "Waiting for command to complete..." msgstr "Väntar på att kommandot ska slutföras..." diff --git a/applications/luci-app-ddns/po/sv/ddns.po b/applications/luci-app-ddns/po/sv/ddns.po index 780a2f9c8f..9373fea3a4 100644 --- a/applications/luci-app-ddns/po/sv/ddns.po +++ b/applications/luci-app-ddns/po/sv/ddns.po @@ -1,5 +1,5 @@ msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" +msgstr "Content-Type: text/plain; charset=UTF-8\n" msgid "&" msgstr "&" @@ -237,8 +237,8 @@ msgid "" "GNU Wget will use the IP of given network, cURL will use the physical " "interface." msgstr "" -"GNU Wget kommer att använda IP-adressen för det angivna nätverket, cURL kommer att använda det fysiska " -"gränssnittet." +"GNU Wget kommer att använda IP-adressen för det angivna nätverket, cURL " +"kommer att använda det fysiska gränssnittet." msgid "Global Settings" msgstr "Globala inställningar" @@ -277,7 +277,8 @@ msgid "IPv6-Address" msgstr "IPv6-adress" msgid "If both cURL and GNU Wget are installed, Wget is used by default." -msgstr "Om både cURL och GNU Wget är installerade så används Wget som standard." +msgstr "" +"Om både cURL och GNU Wget är installerade så används Wget som standard." msgid "" "If this service section is disabled it could not be started.<br />Neither " @@ -319,7 +320,9 @@ msgid "" msgstr "" msgid "It is NOT recommended for casual users to change settings on this page." -msgstr "Det är INTE rekommenderat för vanliga användare att ändra inställningar på den här sidan." +msgstr "" +"Det är INTE rekommenderat för vanliga användare att ändra inställningar på " +"den här sidan." msgid "Last Update" msgstr "Senaste uppdateringen" @@ -604,7 +607,9 @@ msgid "cURL without Proxy Support" msgstr "cURL utan Proxy-stöd" msgid "can not detect local IP. Please select a different Source combination" -msgstr "kan inte upptäcka lokal IP-adress. Vänligen välj en annorlunda Käll-kombination" +msgstr "" +"kan inte upptäcka lokal IP-adress. Vänligen välj en annorlunda Käll-" +"kombination" msgid "can not resolve host:" msgstr "kan inte avgöra värd:" diff --git a/applications/luci-app-diag-core/po/sv/diag_core.po b/applications/luci-app-diag-core/po/sv/diag_core.po index b5679655d0..c31433287f 100644 --- a/applications/luci-app-diag-core/po/sv/diag_core.po +++ b/applications/luci-app-diag-core/po/sv/diag_core.po @@ -29,5 +29,5 @@ msgid "" "With this menu you can configure network diagnostics, such as network device " "scans and ping tests." msgstr "" -"Med den här menyn så kan du ställa in nätverksdiagnostik så som igenomsökningar och " -"ping-tester för nätverksenheten." +"Med den här menyn så kan du ställa in nätverksdiagnostik så som " +"igenomsökningar och ping-tester för nätverksenheten." diff --git a/applications/luci-app-firewall/luasrc/model/cbi/firewall/zone-details.lua b/applications/luci-app-firewall/luasrc/model/cbi/firewall/zone-details.lua index 500d1bf32f..7553504572 100644 --- a/applications/luci-app-firewall/luasrc/model/cbi/firewall/zone-details.lua +++ b/applications/luci-app-firewall/luasrc/model/cbi/firewall/zone-details.lua @@ -21,7 +21,7 @@ nw.init(m.uci) local zone = fw:get_zone(arg[1]) if not zone then - luci.http.redirect(dsp.build_url("admin/network/firewall/zones")) + luci.http.redirect(ds.build_url("admin/network/firewall/zones")) return else m.title = "%s - %s" %{ diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua index 604a4fa842..aeabc63616 100644 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua @@ -111,7 +111,7 @@ mwan_interface = m5:section(TypedSection, "interface", translate("Interfaces"), "Interfaces may not share the same name as configured members, policies or rules")) mwan_interface.addremove = true mwan_interface.dynamic = false - mwan_interface.sectionhead = "Interface" + mwan_interface.sectionhead = translate("Interface") mwan_interface.sortable = false mwan_interface.template = "cbi/tblsection" mwan_interface.extedit = dsp.build_url("admin", "network", "mwan", "configuration", "interface", "%s") diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua index 3bccbd942f..efbe8f7902 100644 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua @@ -13,7 +13,7 @@ mwan_member = m5:section(TypedSection, "member", translate("Members"), "Members may not share the same name as configured interfaces, policies or rules")) mwan_member.addremove = true mwan_member.dynamic = false - mwan_member.sectionhead = "Member" + mwan_member.sectionhead = translate("Member") mwan_member.sortable = true mwan_member.template = "cbi/tblsection" mwan_member.extedit = ds.build_url("admin", "network", "mwan", "configuration", "member", "%s") diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua index 08c3f69de6..6640564d50 100644 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua @@ -42,7 +42,7 @@ mwan_policy = m5:section(TypedSection, "policy", translate("Policies"), "Policies may not share the same name as configured interfaces, members or rules")) mwan_policy.addremove = true mwan_policy.dynamic = false - mwan_policy.sectionhead = "Policy" + mwan_policy.sectionhead = translate("Policy") mwan_policy.sortable = true mwan_policy.template = "cbi/tblsection" mwan_policy.extedit = ds.build_url("admin", "network", "mwan", "configuration", "policy", "%s") @@ -65,7 +65,6 @@ use_member = mwan_policy:option(DummyValue, "use_member", translate("Members ass else return "—" end - end last_resort = mwan_policy:option(DummyValue, "last_resort", translate("Last resort")) @@ -73,11 +72,11 @@ last_resort = mwan_policy:option(DummyValue, "last_resort", translate("Last reso function last_resort.cfgvalue(self, s) local action = self.map:get(s, "last_resort") if action == "blackhole" then - return "blackhole (drop)" + return translate("blackhole (drop)") elseif action == "default" then - return "default (use main routing table)" + return translate("default (use main routing table)") else - return "unreachable (reject)" + return translate("unreachable (reject)") end end diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua index 412f369eb0..0f4c5950a0 100644 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua @@ -47,7 +47,7 @@ mwan_rule = m5:section(TypedSection, "rule", translate("Traffic Rules"), mwan_rule.addremove = true mwan_rule.anonymous = false mwan_rule.dynamic = false - mwan_rule.sectionhead = "Rule" + mwan_rule.sectionhead = translate("Rule") mwan_rule.sortable = true mwan_rule.template = "cbi/tblsection" mwan_rule.extedit = dsp.build_url("admin", "network", "mwan", "configuration", "rule", "%s") @@ -93,10 +93,10 @@ sticky = mwan_rule:option(DummyValue, "sticky", translate("Sticky")) function sticky.cfgvalue(self, s) if self.map:get(s, "sticky") == "1" then stickied = 1 - return "Yes" + return translate("Yes") else stickied = nil - return "No" + return translate("No") end end @@ -133,7 +133,7 @@ errors = mwan_rule:option(DummyValue, "errors", translate("Errors")) if not string.find(error_protocol_list, " " .. s .. " ") then return "" else - return "<span title=\"No protocol specified\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>" + return "<span title=\"" .. translate("No protocol specified") .. "\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>" end end diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/advanced_diagnostics.htm b/applications/luci-app-mwan3/luasrc/view/mwan/advanced_diagnostics.htm index 6f350ccf4d..4483485946 100644 --- a/applications/luci-app-mwan3/luasrc/view/mwan/advanced_diagnostics.htm +++ b/applications/luci-app-mwan3/luasrc/view/mwan/advanced_diagnostics.htm @@ -24,7 +24,7 @@ <script type="text/javascript">//<![CDATA[ var stxhr = new XHR(); - function update_status(tool, task) + function update_status(tool, task, task_name) { var iface = document.getElementById('mwaniface').value; var output = document.getElementById('diag_output'); @@ -33,14 +33,14 @@ { output.innerHTML = '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' + - "Waiting for MWAN to " + task + "..." + String.format("<%:Waiting for MWAN to %s...%>", task_name) ; } else { output.innerHTML = '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' + - "Waiting for diagnostic results..." + "<%:Waiting for diagnostic results...%>" ; } @@ -56,7 +56,7 @@ } else { - output.innerHTML = '<pre id="diag_output_css"><strong>No diagnostic results returned</strong></pre>'; + output.innerHTML = '<pre id="diag_output_css"><strong><%:No diagnostic results returned%></strong></pre>'; } } ); @@ -70,20 +70,20 @@ <% for z in interfaceNames:gmatch("[^ ]+") do -%><option value="<%=z%>"><%=z%></option><%- end %> </select> <div id="buttoncss"> - <input type="button" value="<%:Ping default gateway%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'gateway')" /> - <input type="button" value="<%:Ping tracking IP%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'track_ip')" /> - <input type="button" value="<%:Check IP rules%>" class="cbi-button cbi-button-apply" onclick="update_status('rulechk', null)" /> - <input type="button" value="<%:Check routing table%>" class="cbi-button cbi-button-apply" onclick="update_status('routechk', null)" /> - <input type="button" value="<%:Hotplug ifup%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifup')" /> - <input type="button" value="<%:Hotplug ifdown%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifdown')" /> + <input type="button" value="<%:Ping default gateway%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'gateway', null)" /> + <input type="button" value="<%:Ping tracking IP%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'track_ip', null)" /> + <input type="button" value="<%:Check IP rules%>" class="cbi-button cbi-button-apply" onclick="update_status('rulechk', null, null)" /> + <input type="button" value="<%:Check routing table%>" class="cbi-button cbi-button-apply" onclick="update_status('routechk', null, null)" /> + <input type="button" value="<%:Hotplug ifup%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifup', null)" /> + <input type="button" value="<%:Hotplug ifdown%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifdown', null)" /> </div> </fieldset> <fieldset id="diag_select" class="cbi-section"> <legend><%:MWAN Service Control%></legend> <div id="buttoncss"> - <input type="button" value="<%:Restart MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'restart')" /> - <input type="button" value="<%:Stop MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'stop')" /> - <input type="button" value="<%:Start MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'start')" /> + <input type="button" value="<%:Restart MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'restart', '<%:restart%>')" /> + <input type="button" value="<%:Stop MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'stop', '<%:stop%>')" /> + <input type="button" value="<%:Start MWAN%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'start', '<%:start%>')" /> </div> </fieldset> <fieldset class="cbi-section" style="display:none"> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/advanced_troubleshooting.htm b/applications/luci-app-mwan3/luasrc/view/mwan/advanced_troubleshooting.htm index 21f516b7c2..4174ef4b21 100644 --- a/applications/luci-app-mwan3/luasrc/view/mwan/advanced_troubleshooting.htm +++ b/applications/luci-app-mwan3/luasrc/view/mwan/advanced_troubleshooting.htm @@ -37,7 +37,7 @@ } else { - tshoot.innerHTML = '<strong>Error collecting troubleshooting information</strong>'; + tshoot.innerHTML = '<strong><%:Error collecting troubleshooting information%></strong>'; } } ); @@ -46,7 +46,7 @@ <div id="troubleshoot"> <fieldset class="cbi-section"> <legend><%:Troubleshooting Data%></legend> - <div id="troubleshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div> + <div id="troubleshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div> </fieldset> </div> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/advanced_wirelessconfig.htm b/applications/luci-app-mwan3/luasrc/view/mwan/advanced_wirelessconfig.htm index 5077674185..bb18d53493 100644 --- a/applications/luci-app-mwan3/luasrc/view/mwan/advanced_wirelessconfig.htm +++ b/applications/luci-app-mwan3/luasrc/view/mwan/advanced_wirelessconfig.htm @@ -8,17 +8,7 @@ </ul> <style type="text/css"> - .container { /* container for entire page. fixes bootstrap theme's ridiculously small page width */ - max-width: none; - margin: 0px 0px 0px 30px; - padding-right: 30px; - width: auto; - } - .cbi-section-node { - margin-top: 20px; - } .cbi-section { - border: 1px dotted #555555; padding: 20px; } </style> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/openwrt_overview_status.htm b/applications/luci-app-mwan3/luasrc/view/mwan/openwrt_overview_status.htm index 84b1245021..7cef0630ee 100644 --- a/applications/luci-app-mwan3/luasrc/view/mwan/openwrt_overview_status.htm +++ b/applications/luci-app-mwan3/luasrc/view/mwan/openwrt_overview_status.htm @@ -13,19 +13,19 @@ switch (mArray.wans[i].status) { case 'online': - stat = 'Online (tracking active)'; + stat = '<%:Online (tracking active)%>'; cssc = 'wanon'; break; case 'notMonitored': - stat = 'Online (tracking off)'; + stat = '<%:Online (tracking off)%>'; cssc = 'wanon'; break; case 'offline': - stat = 'Offline'; + stat = '<%:Offline%>'; cssc = 'wanoff'; break; case 'notEnabled': - stat = 'Disabled'; + stat = '<%:Disabled%>'; cssc = 'wanoff'; break; } @@ -38,7 +38,7 @@ } else { - status.innerHTML = '<strong>No MWAN interfaces found</strong>'; + status.innerHTML = '<strong><%:No MWAN interfaces found%></strong>'; } } ); @@ -46,7 +46,7 @@ <fieldset id="interface_field" class="cbi-section"> <legend><%:MWAN Interface Live Status%></legend> - <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div> + <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div> </fieldset> <style type="text/css"> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/overview_detailed.htm b/applications/luci-app-mwan3/luasrc/view/mwan/overview_detailed.htm index bbb617b289..6a800c3f98 100644 --- a/applications/luci-app-mwan3/luasrc/view/mwan/overview_detailed.htm +++ b/applications/luci-app-mwan3/luasrc/view/mwan/overview_detailed.htm @@ -17,7 +17,7 @@ } else { - status.innerHTML = '<strong>No detailed status information available</strong>'; + status.innerHTML = '<strong><%:No detailed status information available%></strong>'; } } ); @@ -26,7 +26,7 @@ <div id="mwan_detail_status"> <fieldset class="cbi-section"> <legend><%:MWAN Detailed Status%></legend> - <div id="mwan_detail_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div> + <div id="mwan_detail_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div> </fieldset> </div> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/overview_interface.htm b/applications/luci-app-mwan3/luasrc/view/mwan/overview_interface.htm index f4c8b1224b..2929a6df60 100644 --- a/applications/luci-app-mwan3/luasrc/view/mwan/overview_interface.htm +++ b/applications/luci-app-mwan3/luasrc/view/mwan/overview_interface.htm @@ -21,19 +21,19 @@ switch (mArray.wans[i].status) { case 'online': - status = 'Online (tracking active)'; + status = '<%:Online (tracking active)%>'; css = 'wanon'; break; case 'notMonitored': - status = 'Online (tracking off)'; + status = '<%:Online (tracking off)%>'; css = 'wanon'; break; case 'offline': - status = 'Offline'; + status = '<%:Offline%>'; css = 'wanoff'; break; case 'notEnabled': - status = 'Disabled'; + status = '<%:Disabled%>'; css = 'wanoff'; break; } @@ -46,18 +46,18 @@ } else { - statusDiv.innerHTML = '<strong>No MWAN interfaces found</strong>'; + statusDiv.innerHTML = '<strong><%:No MWAN interfaces found%></strong>'; } var logs = document.getElementById('mwan_statuslog_text'); if (mArray.mwanlog) { - var mwanLog = 'Last 50 MWAN systemlog entries. Newest entries sorted at the top :'; + var mwanLog = '<%:Last 50 MWAN systemlog entries. Newest entries sorted at the top :%>'; logs.innerHTML = String.format('<pre>%s<br /><br />%s</pre>', mwanLog, mArray.mwanlog[0]); } else { - logs.innerHTML = '<strong>No MWAN systemlog history found</strong>'; + logs.innerHTML = '<strong><%:No MWAN systemlog history found%></strong>'; } } ); @@ -66,11 +66,11 @@ <div id="mwan_interface_status"> <fieldset id="interface_field" class="cbi-section"> <legend><%:MWAN Interface Live Status%></legend> - <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div> + <div id="mwan_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div> </fieldset> <fieldset class="cbi-section"> <legend><%:MWAN Interface Systemlog%></legend> - <div id="mwan_statuslog_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div> + <div id="mwan_statuslog_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /><%:Collecting data...%></div> </fieldset> </div> diff --git a/applications/luci-app-mwan3/po/ja/mwan3.po b/applications/luci-app-mwan3/po/ja/mwan3.po index 834a82053c..f6a70c3561 100644 --- a/applications/luci-app-mwan3/po/ja/mwan3.po +++ b/applications/luci-app-mwan3/po/ja/mwan3.po @@ -7,7 +7,7 @@ msgstr "" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.0.2\n" +"X-Generator: Poedit 2.0.3\n" "Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n" "Plural-Forms: nplurals=1; plural=0;\n" "Language: ja\n" @@ -46,6 +46,9 @@ msgstr "IP ルールのチェック" msgid "Check routing table" msgstr "ルーティング テーブルのチェック" +msgid "Collecting data..." +msgstr "データ収集中です..." + msgid "Configuration" msgstr "設定" @@ -73,6 +76,9 @@ msgstr "診断結果" msgid "Diagnostics" msgstr "診断機能" +msgid "Disabled" +msgstr "無効" + msgid "" "Downed interface will be deemed up after this many successful ping tests" msgstr "" @@ -82,6 +88,9 @@ msgstr "" msgid "Enabled" msgstr "有効" +msgid "Error collecting troubleshooting information" +msgstr "トラブルシューティング情報の収集エラー" + msgid "Errors" msgstr "エラー" @@ -135,6 +144,9 @@ msgstr "インターフェース" msgid "Internet Protocol" msgstr "インターネット プロトコル" +msgid "Last 50 MWAN systemlog entries. Newest entries sorted at the top :" +msgstr "直近の MWAN システムログ(50行)です。一番上が最新の行です:" + msgid "Last resort" msgstr "最終手段" @@ -209,6 +221,9 @@ msgstr "" "単一または複数のポート(例: \"22\" または \"80,443\")、あるいはポートの範囲" "(例: \"1024:2048\")を、クオーテーション無しで指定することができます。" +msgid "Member" +msgstr "メンバー" + msgid "Member used" msgstr "使用されるメンバー" @@ -244,6 +259,30 @@ msgstr "ネットワーク設定" msgid "No" msgstr "いいえ" +msgid "No MWAN interfaces found" +msgstr "MWAN インターフェースが見つかりません" + +msgid "No MWAN systemlog history found" +msgstr "MWAN システムログの履歴が見つかりません" + +msgid "No detailed status information available" +msgstr "詳細ステータス情報は利用できません" + +msgid "No diagnostic results returned" +msgstr "診断結果がありません" + +msgid "No protocol specified" +msgstr "プロトコルが設定されていません" + +msgid "Offline" +msgstr "オフライン" + +msgid "Online (tracking active)" +msgstr "オンライン(追跡実行中)" + +msgid "Online (tracking off)" +msgstr "オンライン(追跡オフ)" + msgid "Overview" msgstr "概要" @@ -292,6 +331,9 @@ msgstr "" "ん。また、15文字以内でなければなりません。<br />ポリシーでは、設定済みのイン" "ターフェースやメンバー、ルールと同じ名前を使用することはできません。" +msgid "Policy" +msgstr "ポリシー" + msgid "Policy assigned" msgstr "アサイン済みポリシー" @@ -310,6 +352,9 @@ msgstr "デフォルトのホットプラグ スクリプトの復元" msgid "Restore..." msgstr "復元..." +msgid "Rule" +msgstr "ルール" + msgid "Rules" msgstr "ルール" @@ -362,6 +407,11 @@ msgid "There are currently %d of 250 supported interfaces configured" msgstr "現在、250個中 %d 個のサポートされたインターフェースが設定済みです。" msgid "" +"This displays the metric assigned to this interface in /etc/config/network" +msgstr "" +"/etc/config/network で、このインターフェースに割り当てられたメトリックです。" + +msgid "" "This hostname or IP address will be pinged to determine if the link is up or " "down. Leave blank to assume interface is always online" msgstr "" @@ -369,11 +419,6 @@ msgstr "" "に対して Ping の送信が行われます。常にオンラインとする場合、空欄のままにしま" "す。" -msgid "" -"This displays the metric assigned to this interface in /etc/config/network" -msgstr "" -"/etc/config/network で、このインターフェースに割り当てられたメトリックです。" - msgid "This section allows you to modify the contents of /etc/config/mwan3" msgstr "" "このセクションでは、 /etc/config/mwan3 の内容を変更することができます。" @@ -406,12 +451,12 @@ msgstr "" "wan2, その他)<br />$DEVICE - インターフェースにアタッチされたデバイスの名前" "(eth0.1, eth1, その他)" -msgid "Tracking hostname or IP address" -msgstr "追跡ホスト名または IP アドレス" - msgid "Tracking IP" msgstr "追跡 IP" +msgid "Tracking hostname or IP address" +msgstr "追跡ホスト名または IP アドレス" + msgid "Tracking reliability" msgstr "追跡の信頼性" @@ -529,6 +574,12 @@ msgstr "" "警告: このルールは不適切なプロトコルが指定されているか、または何も指定されて" "いません!プロトコルを指定し直してください!" +msgid "Waiting for MWAN to %s..." +msgstr "MWAN の %s を待っています..." + +msgid "Waiting for diagnostic results..." +msgstr "診断結果を待っています..." + msgid "Weight" msgstr "ウエイト" @@ -562,6 +613,15 @@ msgstr "ifup" msgid "never" msgstr "never" +msgid "restart" +msgstr "再起動" + +msgid "start" +msgstr "起動" + +msgid "stop" +msgstr "停止" + msgid "unreachable (reject)" msgstr "unreachable (reject)" diff --git a/applications/luci-app-mwan3/po/templates/mwan3.pot b/applications/luci-app-mwan3/po/templates/mwan3.pot index 3040d2661a..3d25e844ac 100644 --- a/applications/luci-app-mwan3/po/templates/mwan3.pot +++ b/applications/luci-app-mwan3/po/templates/mwan3.pot @@ -33,6 +33,9 @@ msgstr "" msgid "Check routing table" msgstr "" +msgid "Collecting data..." +msgstr "" + msgid "Configuration" msgstr "" @@ -60,6 +63,9 @@ msgstr "" msgid "Diagnostics" msgstr "" +msgid "Disabled" +msgstr "" + msgid "" "Downed interface will be deemed up after this many successful ping tests" msgstr "" @@ -67,6 +73,9 @@ msgstr "" msgid "Enabled" msgstr "" +msgid "Error collecting troubleshooting information" +msgstr "" + msgid "Errors" msgstr "" @@ -118,6 +127,9 @@ msgstr "" msgid "Internet Protocol" msgstr "" +msgid "Last 50 MWAN systemlog entries. Newest entries sorted at the top :" +msgstr "" + msgid "Last resort" msgstr "" @@ -183,6 +195,9 @@ msgid "" "as a portrange (eg \"1024:2048\") without quotes" msgstr "" +msgid "Member" +msgstr "" + msgid "Member used" msgstr "" @@ -212,6 +227,30 @@ msgstr "" msgid "No" msgstr "" +msgid "No MWAN interfaces found" +msgstr "" + +msgid "No MWAN systemlog history found" +msgstr "" + +msgid "No detailed status information available" +msgstr "" + +msgid "No diagnostic results returned" +msgstr "" + +msgid "No protocol specified" +msgstr "" + +msgid "Offline" +msgstr "" + +msgid "Online (tracking active)" +msgstr "" + +msgid "Online (tracking off)" +msgstr "" + msgid "Overview" msgstr "" @@ -252,6 +291,9 @@ msgid "" "configured interfaces, members or rules" msgstr "" +msgid "Policy" +msgstr "" + msgid "Policy assigned" msgstr "" @@ -270,6 +312,9 @@ msgstr "" msgid "Restore..." msgstr "" +msgid "Rule" +msgstr "" + msgid "Rules" msgstr "" @@ -312,12 +357,12 @@ msgid "There are currently %d of 250 supported interfaces configured" msgstr "" msgid "" -"This hostname or IP address will be pinged to determine if the link is up or " -"down. Leave blank to assume interface is always online" +"This displays the metric assigned to this interface in /etc/config/network" msgstr "" msgid "" -"This displays the metric assigned to this interface in /etc/config/network" +"This hostname or IP address will be pinged to determine if the link is up or " +"down. Leave blank to assume interface is always online" msgstr "" msgid "This section allows you to modify the contents of /etc/config/mwan3" @@ -340,10 +385,10 @@ msgid "" "device name attached to the interface (eth0.1, eth1, etc.)" msgstr "" -msgid "Tracking hostname or IP address" +msgid "Tracking IP" msgstr "" -msgid "Tracking IP" +msgid "Tracking hostname or IP address" msgstr "" msgid "Tracking reliability" @@ -432,6 +477,12 @@ msgid "" "specified! Please configure a specific protocol!" msgstr "" +msgid "Waiting for MWAN to %s..." +msgstr "" + +msgid "Waiting for diagnostic results..." +msgstr "" + msgid "Weight" msgstr "" @@ -463,5 +514,14 @@ msgstr "" msgid "never" msgstr "" +msgid "restart" +msgstr "" + +msgid "start" +msgstr "" + +msgid "stop" +msgstr "" + msgid "unreachable (reject)" msgstr "" diff --git a/applications/luci-app-mwan3/po/zh-cn/mwan3.po b/applications/luci-app-mwan3/po/zh-cn/mwan3.po index d1066c5850..1e0f34f088 100644 --- a/applications/luci-app-mwan3/po/zh-cn/mwan3.po +++ b/applications/luci-app-mwan3/po/zh-cn/mwan3.po @@ -1,17 +1,7 @@ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Language-Team: \n" "Last-Translator: Hsing-Wang Liao <kuoruan@gmail.com>\n" -"MIME-Version: 1.0\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.0\n" -"Last-Translator: \n" -"Plural-Forms: nplurals=1; plural=0;\n" -"Language: zh_CN\n" msgid "%d hour" msgstr "%d 小时" @@ -32,20 +22,23 @@ msgid "" "Acceptable values: 1-100. This many Tracking IP addresses must respond for " "the link to be deemed up" msgstr "" -"接受的值: 1-100。这个设置项指定了当多少个IP地址能够连通时接口会被认为在线" +"取值范围: 1-100。这个设置项指定了当多少个 IP 地址能够连通时接口会被认为在线" msgid "Acceptable values: 1-1000. Defaults to 1 if not set" -msgstr "接受的值: 1-100。如果不填写,默认值为 1" +msgstr "取值范围: 1-100。如果不填写,默认值为 1" msgid "Advanced" msgstr "高级" msgid "Check IP rules" -msgstr "检查IP规则" +msgstr "检查 IP 规则" msgid "Check routing table" msgstr "检查路由表" +msgid "Collecting data..." +msgstr "正在收集数据..." + msgid "Configuration" msgstr "配置" @@ -73,6 +66,9 @@ msgstr "诊断结果" msgid "Diagnostics" msgstr "诊断" +msgid "Disabled" +msgstr "禁用" + msgid "" "Downed interface will be deemed up after this many successful ping tests" msgstr "当 Ping 成功次数达到这个数值后,已经被认为离线的接口将会重新上线" @@ -80,6 +76,9 @@ msgstr "当 Ping 成功次数达到这个数值后,已经被认为离线的接 msgid "Enabled" msgstr "启用" +msgid "Error collecting troubleshooting information" +msgstr "收集故障排除信息时出错" + msgid "Errors" msgstr "错误" @@ -131,6 +130,9 @@ msgstr "接口" msgid "Internet Protocol" msgstr "互联网协议" +msgid "Last 50 MWAN systemlog entries. Newest entries sorted at the top :" +msgstr "最近 50 条 MWAN 系统日志,最新条目排在顶部:" + msgid "Last resort" msgstr "备用成员" @@ -192,15 +194,19 @@ msgid "" msgstr "" "MWAN 支持最多 250 个物理或逻辑接口。<br />MWAN 要求所有接口必须在 /etc/" "config/network 中设定唯一的网关跃点。<br />名称必须与 /etc/config/network 中" -"的接口名称匹配。(可查看“高级”选项卡)<br />名称允许包括A-Z、a-z、0-9、_ 但是不" -"能有空格。<br />接口不应该与成员、策略、规则中的任意一个设置项使用相同的名称" +"的接口名称匹配。(可查看“高级”选项卡)<br />名称允许包括A-Z、a-z、0-9、_ 但是" +"不能有空格。<br />接口不应该与成员、策略、规则中的任意一个设置项使用相同的名" +"称" msgid "" "May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or " "as a portrange (eg \"1024:2048\") without quotes" msgstr "" -"可以输入一个或多个端口 (例如 \"22\" 或者 \"80,443\") 或者是一个端口范围 (例" -"如 \"1024:2048\") 不含引号" +"可以输入一个或多个端口(例如 \"22\" 或者 \"80,443\")或者是一个端口范围(例" +"如 \"1024:2048\")不含引号" + +msgid "Member" +msgstr "成员" msgid "Member used" msgstr "使用的成员" @@ -213,7 +219,7 @@ msgid "" ">Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />Members " "may not share the same name as configured interfaces, policies or rules" msgstr "" -"“成员”用来设置每一个 MWAN 接口的跃点数 (即接口优先级) 和所占比重。<br />名称" +"“成员”用来设置每一个 MWAN 接口的跃点数(即接口优先级)和所占比重。<br />名称" "允许包括 A-Z、 a-、0-9、_ 但是不能有空格。<br />成员不应该与接口、策略、规则" "中的任意一个设置项使用相同的名称" @@ -236,6 +242,30 @@ msgstr "网络配置文件" msgid "No" msgstr "否" +msgid "No MWAN interfaces found" +msgstr "没有找到 MWAN 接口" + +msgid "No MWAN systemlog history found" +msgstr "没有在系统日志中找到 MWAN 历史信息" + +msgid "No detailed status information available" +msgstr "没有状态详细信息可用" + +msgid "No diagnostic results returned" +msgstr "没有返回诊断结果" + +msgid "No protocol specified" +msgstr "未指定协议" + +msgid "Offline" +msgstr "离线" + +msgid "Online (tracking active)" +msgstr "在线(追踪启用中)" + +msgid "Online (tracking off)" +msgstr "在线(追踪已关闭)" + msgid "Overview" msgstr "概况" @@ -281,6 +311,9 @@ msgstr "" "包括A-Z、a-z、0-9、_ 但是不能有空格。名称应该在 15 个字符以内<br />策略不应该" "与接口、成员、规则中的任意一个设置项使用相同的名称" +msgid "Policy" +msgstr "策略" + msgid "Policy assigned" msgstr "分配的策略" @@ -299,6 +332,9 @@ msgstr "恢复默认的 hotplug 脚本" msgid "Restore..." msgstr "恢复..." +msgid "Rule" +msgstr "规则" + msgid "Rules" msgstr "规则" @@ -342,20 +378,20 @@ msgid "Stop MWAN" msgstr "停止 MWAN" msgid "Supports CIDR notation (eg \"192.168.100.0/24\") without quotes" -msgstr "支持 CIDR 记法 (例如: \"192.168.100.0/24\") 不含引号" +msgstr "支持 CIDR 记法(例如: \"192.168.100.0/24\")不含引号" msgid "There are currently %d of 250 supported interfaces configured" -msgstr "" - -msgid "" -"This hostname or IP address will be pinged to determine if the link is up or " -"down. Leave blank to assume interface is always online" -msgstr "" +msgstr "当前已配置 %d 个接口,最大支持 250 个" msgid "" "This displays the metric assigned to this interface in /etc/config/network" msgstr "这里显示了这个接口在 /etc/config/network 中配置的跃点数" +msgid "" +"This hostname or IP address will be pinged to determine if the link is up or " +"down. Leave blank to assume interface is always online" +msgstr "通过 ping 此主机或 IP 地址来确定链路是否在线。留空则认为接口始终在线" + msgid "This section allows you to modify the contents of /etc/config/mwan3" msgstr "这里允许你修改 /etc/config/mwan3 的内容" @@ -375,21 +411,21 @@ msgid "" "$INTERFACE is the interface name (wan1, wan2, etc.)<br />$DEVICE is the " "device name attached to the interface (eth0.1, eth1, etc.)" msgstr "" -"这里允许你修改/etc/hotplug.d/iface/16-mwancustom 的内容<br />这可以在接口 " +"这里允许你修改 /etc/hotplug.d/iface/16-mwancustom 的内容<br />这可以在接口 " "ifup 或 ifdown Hotplug 事件时运行系统命令或脚本<br /><br />注意:<br />脚本的" "第一行必须是 "#!/bin/sh" 不含引号<br />以#开头的行是注释,不会执行" -"<br /><br />可用变量:<br />$ACTION 是 Hotplug 事件 (ifup, ifdown)<br />" -"$INTERFACE 是接口名称 (wan1、wan2 等)<br />$DEVICE 是连接到接口的设备名称 " -"(eth0.1、eth1 等)" - -msgid "Tracking hostname or IP address" -msgstr "" +"<br /><br />可用变量:<br />$ACTION 是 Hotplug 事件(ifup, ifdown)<br />" +"$INTERFACE 是接口名称(wan1、wan2 等)<br />$DEVICE 是连接到接口的设备名称 " +"(eth0.1、eth1 等)" msgid "Tracking IP" -msgstr "跟踪的 IP" +msgstr "追踪的 IP" + +msgid "Tracking hostname or IP address" +msgstr "追踪的主机或 IP 地址" msgid "Tracking reliability" -msgstr "跟踪可靠性" +msgstr "追踪可靠性" msgid "Traffic Rules" msgstr "流量规则" @@ -411,70 +447,77 @@ msgid "View the contents of /etc/protocols for protocol descriptions" msgstr "请查看 /etc/protocols 获取可选协议详情" msgid "WARNING: %d interfaces are configured exceeding the maximum of 250!" -msgstr "" +msgstr "警告: 已配置 %d 个接口,超过最大值 250!" msgid "" "WARNING: Some policies have names exceeding the maximum of 15 characters!" -msgstr "" +msgstr "警告: 某些策略的名称超过了 15 个字符!" msgid "" "WARNING: some interfaces are configured incorrectly or not at all in /etc/" "config/network!" -msgstr "" +msgstr "警告: 某些接口配置不正确或未配置到 /etc/config/network!" msgid "" "WARNING: some interfaces have a higher reliability requirement than there " "are tracking IP addresses!" -msgstr "" +msgstr "警告: 某些接口的追踪可靠性要求大于了追踪 IP 地址总数!" msgid "" "WARNING: some interfaces have duplicate metrics configured in /etc/config/" "network!" -msgstr "" +msgstr "警告: 某些接口在 /etc/config/network 中配置了相同的跃点数!" msgid "" "WARNING: some interfaces have no default route in the main routing table!" -msgstr "" +msgstr "警告: 某些接口在主路由表中没有默认路由!" msgid "" "WARNING: some interfaces have no metric configured in /etc/config/network!" -msgstr "" +msgstr "警告: 某些接口没有在 /etc/config/network 中配置跃点数!" msgid "" "WARNING: some rules have a port configured with no or improper protocol " "specified! Please configure a specific protocol!" msgstr "" +"警告: 某些规则指定了端口却没有配置或配置了不正确的协议,请重新指定协议!" msgid "" "WARNING: this and other interfaces have duplicate metrics configured in /etc/" "config/network!" -msgstr "" +msgstr "警告: 此接口和其他接口在 /etc/config/network 中配置了相同的跃点数!" msgid "" "WARNING: this interface has a higher reliability requirement than there are " "tracking IP addresses!" -msgstr "" +msgstr "警告: 此接口的追踪可靠性要求大于了追踪 IP 地址总数!" msgid "WARNING: this interface has no default route in the main routing table!" -msgstr "" +msgstr "警告: 此接口在主路由表中没有默认路由!" msgid "" "WARNING: this interface has no metric configured in /etc/config/network!" -msgstr "" +msgstr "警告: 此接口没有在 /etc/config/network 中配置跃点数!" msgid "" "WARNING: this interface is configured incorrectly or not at all in /etc/" "config/network!" -msgstr "" +msgstr "警告: 此接口配置不正确或未配置到 /etc/config/network!" msgid "" "WARNING: this policy's name is %d characters exceeding the maximum of 15!" -msgstr "" +msgstr "警告: 此策略的名称具有 %d 个字符,超过了 15 个字符!" msgid "" "WARNING: this rule is incorrectly configured with no or improper protocol " "specified! Please configure a specific protocol!" -msgstr "" +msgstr "警告: 此规则没有配置或配置了不正确的协议,请重新指定协议!" + +msgid "Waiting for MWAN to %s..." +msgstr "等待 MWAN %s..." + +msgid "Waiting for diagnostic results..." +msgstr "等待诊断结果..." msgid "Weight" msgstr "比重" @@ -493,10 +536,10 @@ msgid "always" msgstr "总是" msgid "blackhole (drop)" -msgstr "黑洞 (丢弃)" +msgstr "黑洞(丢弃)" msgid "default (use main routing table)" -msgstr "默认 (使用主路由表)" +msgstr "默认(使用主路由表)" msgid "ifdown" msgstr "ifdown" @@ -507,12 +550,14 @@ msgstr "ifup" msgid "never" msgstr "从不" +msgid "restart" +msgstr "" + +msgid "start" +msgstr "" + +msgid "stop" +msgstr "" + msgid "unreachable (reject)" -msgstr "不可达 (拒绝)" - -#~ msgid "" -#~ "This IP address will be pinged to dermine if the link is up or down. " -#~ "Leave blank to assume interface is always online" -#~ msgstr "" -#~ "MWAN 将会通过 Ping 这些 IP 地址来确定接口是否上线。如果留空,则 MWAN 认为" -#~ "该接口永远在线" +msgstr "不可达(拒绝)" diff --git a/applications/luci-app-nlbwmon/Makefile b/applications/luci-app-nlbwmon/Makefile new file mode 100644 index 0000000000..a00177f2ca --- /dev/null +++ b/applications/luci-app-nlbwmon/Makefile @@ -0,0 +1,8 @@ +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=Netlink based bandwidth accounting +LUCI_DEPENDS:=+nlbwmon + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-nlbwmon/htdocs/luci-static/resources/nlbw.chart.min.js b/applications/luci-app-nlbwmon/htdocs/luci-static/resources/nlbw.chart.min.js new file mode 100644 index 0000000000..34e3026825 --- /dev/null +++ b/applications/luci-app-nlbwmon/htdocs/luci-static/resources/nlbw.chart.min.js @@ -0,0 +1,68 @@ +(function(){var p=this,l=p.Chart,e=function(a){this.canvas=a.canvas;this.ctx=a;var b=function(a,b){return a["offset"+b]?a["offset"+b]:document.defaultView.getComputedStyle(a).getPropertyValue(b)};this.width=b(a.canvas,"Width")||a.canvas.width;this.height=b(a.canvas,"Height")||a.canvas.height;this.width=a.canvas.width;this.height=a.canvas.height;this.aspectRatio=this.width/this.height;d.retinaScale(this);return this};e.defaults={global:{animation:!0,animationSteps:60,animationEasing:"easeOutQuart", +showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:!1,maintainAspectRatio:!0,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)", +tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipTitleTemplate:"<%= label%>",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= value %>", +multiTooltipKeyBackground:"#fff",segmentColorDefault:"#A6CEE3 #1F78B4 #B2DF8A #33A02C #FB9A99 #E31A1C #FDBF6F #FF7F00 #CAB2D6 #6A3D9A #B4B482 #B15928".split(" "),segmentHighlightColorDefaults:"#CEF6FF #47A0DC #DAFFB2 #5BC854 #FFC2C1 #FF4244 #FFE797 #FFA728 #F2DAFE #9265C2 #DCDCAA #D98150".split(" "),onAnimationProgress:function(){},onAnimationComplete:function(){}}};e.types={};var d=e.helpers={},k=d.each=function(a,b,c){var f=Array.prototype.slice.call(arguments,3);if(a)if(a.length===+a.length){var d; +for(d=0;d<a.length;d++)b.apply(c,[a[d],d].concat(f))}else for(d in a)b.apply(c,[a[d],d].concat(f))},h=d.clone=function(a){var b={};k(a,function(c,f){a.hasOwnProperty(f)&&(b[f]=c)});return b},r=d.extend=function(a){k(Array.prototype.slice.call(arguments,1),function(b){k(b,function(c,f){b.hasOwnProperty(f)&&(a[f]=c)})});return a},I=d.merge=function(a,b){var c=Array.prototype.slice.call(arguments,0);c.unshift({});return r.apply(null,c)},J=d.indexOf=function(a,b){if(Array.prototype.indexOf)return a.indexOf(b); +for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1};d.where=function(a,b){var c=[];d.each(a,function(a){b(a)&&c.push(a)});return c};d.findNextWhere=function(a,b,c){c||(c=-1);for(c+=1;c<a.length;c++){var f=a[c];if(b(f))return f}};d.findPreviousWhere=function(a,b,c){c||(c=a.length);for(--c;0<=c;c--){var f=a[c];if(b(f))return f}};var D=d.inherits=function(a){var b=this,c=a&&a.hasOwnProperty("constructor")?a.constructor:function(){return b.apply(this,arguments)},f=function(){this.constructor=c}; +f.prototype=b.prototype;c.prototype=new f;c.extend=D;a&&r(c.prototype,a);c.__super__=b.prototype;return c},A=d.noop=function(){},K=d.uid=function(){var a=0;return function(){return"chart-"+a++}}(),L=d.warn=function(a){window.console&&"function"===typeof window.console.warn&&console.warn(a)},M=d.amd="function"===typeof define&&define.amd,u=d.isNumber=function(a){return!isNaN(parseFloat(a))&&isFinite(a)},y=d.max=function(a){return Math.max.apply(Math,a)},w=d.min=function(a){return Math.min.apply(Math, +a)};d.cap=function(a,b,c){if(u(b)){if(a>b)return b}else if(u(c)&&a<c)return c;return a};var E=d.getDecimalPlaces=function(a){if(0!==a%1&&u(a)){a=a.toString();if(0>a.indexOf("e-"))return a.split(".")[1].length;if(0>a.indexOf("."))return parseInt(a.split("e-")[1]);a=a.split(".")[1].split("e-");return a[0].length+parseInt(a[1])}return 0},B=d.radians=function(a){return Math.PI/180*a};d.getAngleFromPoint=function(a,b){var c=b.x-a.x,f=b.y-a.y,d=Math.sqrt(c*c+f*f),m=2*Math.PI+Math.atan2(f,c);0>c&&0>f&&(m+= +2*Math.PI);return{angle:m,distance:d}};var F=d.aliasPixel=function(a){return 0===a%2?0:.5};d.splineCurve=function(a,b,c,f){var d=Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2)),m=Math.sqrt(Math.pow(c.x-b.x,2)+Math.pow(c.y-b.y,2)),g=f*d/(d+m);f=f*m/(d+m);return{inner:{x:b.x-g*(c.x-a.x),y:b.y-g*(c.y-a.y)},outer:{x:b.x+f*(c.x-a.x),y:b.y+f*(c.y-a.y)}}};var N=d.calculateOrderOfMagnitude=function(a){return Math.floor(Math.log(a)/Math.LN10)};d.calculateScaleRange=function(a,b,c,f,d){b=Math.floor(b/(1.5* +c));c=2>=b;var m=[];k(a,function(a){null==a||m.push(a)});var g=w(m),e=y(m);e===g&&(e+=.5,.5<=g&&!f?g-=.5:e+=.5);a=N(Math.abs(e-g));f=f?0:Math.floor(g/(1*Math.pow(10,a)))*Math.pow(10,a);for(var e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-f,g=Math.pow(10,a),n=Math.round(e/g);(n>b||2*n<b)&&!c;)if(n>b)g*=2,n=Math.round(e/g),0!==n%1&&(c=!0);else if(d&&0<=a)if(0===g/2%1)g/=2,n=Math.round(e/g);else break;else g/=2,n=Math.round(e/g);c&&(n=2,g=e/n);return{steps:n,stepValue:g,min:f,max:f+n*g}};var t=d.template= +function(a,b){if(a instanceof Function)return a(b);var c={},c=/\W/.test(a)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):c[a]=c[a];return b?c(b):c};d.generateLabels=function(a,b,c,f){var d=Array(b);a&&k(d,function(b,e){d[e]=t(a,{value:c+ +f*(e+1)})});return d};var x=d.easingEffects={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=.5)?.5*a*a:-.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=.5)?.5*a*a*a:.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1> +(a/=.5)?.5*a*a*a*a:-.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=.5)?.5*a*a*a*a*a:.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0===a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1=== +a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0===a?0:1===a?1:1>(a/=.5)?.5*Math.pow(2,10*(a-1)):.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=.5)?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var b=1.70158,c=0,f=1;if(0===a)return 0;if(1==(a/=1))return 1;c||(c=.3);f<Math.abs(1)?(f=1,b=c/4):b=c/(2*Math.PI)* +Math.asin(1/f);return-(f*Math.pow(2,10*--a)*Math.sin(2*(1*a-b)*Math.PI/c))},easeOutElastic:function(a){var b=1.70158,c=0,f=1;if(0===a)return 0;if(1==(a/=1))return 1;c||(c=.3);f<Math.abs(1)?(f=1,b=c/4):b=c/(2*Math.PI)*Math.asin(1/f);return f*Math.pow(2,-10*a)*Math.sin(2*(1*a-b)*Math.PI/c)+1},easeInOutElastic:function(a){var b=1.70158,c=0,f=1;if(0===a)return 0;if(2==(a/=.5))return 1;c||(c=.3*1.5);f<Math.abs(1)?(f=1,b=c/4):b=c/(2*Math.PI)*Math.asin(1/f);return 1>a?-.5*f*Math.pow(2,10*--a)*Math.sin(2* +(1*a-b)*Math.PI/c):f*Math.pow(2,-10*--a)*Math.sin(2*(1*a-b)*Math.PI/c)*.5+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var b=1.70158;return 1>(a/=.5)?.5*a*a*(((b*=1.525)+1)*a-b):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},easeInBounce:function(a){return 1-x.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)*a+.75):a<2.5/2.75?1*(7.5625* +(a-=2.25/2.75)*a+.9375):1*(7.5625*(a-=2.625/2.75)*a+.984375)},easeInOutBounce:function(a){return.5>a?.5*x.easeInBounce(2*a):.5*x.easeOutBounce(2*a-1)+.5}},G=d.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){return window.setTimeout(a,1E3/60)}}();d.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame|| +window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(a){return window.clearTimeout(a,1E3/60)}}();d.animationLoop=function(a,b,c,f,d,e){var g=0,k=x[c]||x.linear,n=function(){g++;var c=g/b,h=k(c);a.call(e,h,c,g);f.call(e,h,c);g<b?e.animationFrame=G(n):d.apply(e)};G(n)};d.getRelativePosition=function(a){var b;b=a.originalEvent||a;var c=(a.currentTarget||a.srcElement).getBoundingClientRect();b.touches?(a=b.touches[0].clientX-c.left,b=b.touches[0].clientY- +c.top):(a=b.clientX-c.left,b=b.clientY-c.top);return{x:a,y:b}};var O=d.addEvent=function(a,b,c){a.addEventListener?a.addEventListener(b,c):a.attachEvent?a.attachEvent("on"+b,c):a["on"+b]=c},P=d.removeEvent=function(a,b,c){a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent?a.detachEvent("on"+b,c):a["on"+b]=A};d.bindEvents=function(a,b,c){a.events||(a.events={});k(b,function(b){a.events[b]=function(){c.apply(a,arguments)};O(a.chart.canvas,b,a.events[b])})};var Q=d.unbindEvents=function(a, +b){k(b,function(b,f){P(a.chart.canvas,f,b)})},R=d.getMaximumWidth=function(a){a=a.parentNode;var b=parseInt(z(a,"padding-left"))+parseInt(z(a,"padding-right"));return a?a.clientWidth-b:0},S=d.getMaximumHeight=function(a){a=a.parentNode;var b=parseInt(z(a,"padding-bottom"))+parseInt(z(a,"padding-top"));return a?a.clientHeight-b:0},z=d.getStyle=function(a,b){return a.currentStyle?a.currentStyle[b]:document.defaultView.getComputedStyle(a,null).getPropertyValue(b)};d.getMaximumSize=d.getMaximumWidth; +var T=d.retinaScale=function(a){var b=a.ctx,c=a.canvas.width;a=a.canvas.height;window.devicePixelRatio&&(b.canvas.style.width=c+"px",b.canvas.style.height=a+"px",b.canvas.height=a*window.devicePixelRatio,b.canvas.width=c*window.devicePixelRatio,b.scale(window.devicePixelRatio,window.devicePixelRatio))},U=d.clear=function(a){a.ctx.clearRect(0,0,a.width,a.height)},v=d.fontString=function(a,b,c){return b+" "+a+"px "+c},C=d.longestText=function(a,b,c){a.font=b;var f=0;k(c,function(b){b=a.measureText(b).width; +f=b>f?b:f});return f},H=d.drawRoundedRectangle=function(a,b,c,f,d,e){a.beginPath();a.moveTo(b+e,c);a.lineTo(b+f-e,c);a.quadraticCurveTo(b+f,c,b+f,c+e);a.lineTo(b+f,c+d-e);a.quadraticCurveTo(b+f,c+d,b+f-e,c+d);a.lineTo(b+e,c+d);a.quadraticCurveTo(b,c+d,b,c+d-e);a.lineTo(b,c+e);a.quadraticCurveTo(b,c,b+e,c);a.closePath()};e.instances={};e.Type=function(a,b,c){this.options=b;this.chart=c;this.id=K();e.instances[this.id]=this;b.responsive&&this.resize();this.initialize.call(this,a)};r(e.Type.prototype, +{initialize:function(){return this},clear:function(){U(this.chart);return this},stop:function(){e.animationService.cancelAnimation(this);return this},resize:function(a){this.stop();var b=this.chart.canvas,c=R(this.chart.canvas),f=this.options.maintainAspectRatio?c/this.chart.aspectRatio:S(this.chart.canvas);b.width=this.chart.width=c;b.height=this.chart.height=f;T(this.chart);"function"===typeof a&&a.apply(this,Array.prototype.slice.call(arguments,1));return this},reflow:A,render:function(a){a&&this.reflow(); +this.options.animation&&!a?(a=new e.Animation,a.numSteps=this.options.animationSteps,a.easing=this.options.animationEasing,a.render=function(a,c){var f=c.currentStep/c.numSteps,e=(0,d.easingEffects[c.easing])(f);a.draw(e,f,c.currentStep)},a.onAnimationProgress=this.options.onAnimationProgress,a.onAnimationComplete=this.options.onAnimationComplete,e.animationService.addAnimation(this,a)):(this.draw(),this.options.onAnimationComplete.call(this));return this},generateLegend:function(){return t(this.options.legendTemplate, +this)},destroy:function(){this.clear();Q(this,this.events);var a=this.chart.canvas;a.width=this.chart.width;a.height=this.chart.height;a.style.removeProperty?(a.style.removeProperty("width"),a.style.removeProperty("height")):(a.style.removeAttribute("width"),a.style.removeAttribute("height"));delete e.instances[this.id]},showTooltip:function(a,b){"undefined"===typeof this.activeElements&&(this.activeElements=[]);if(function(a){var b=!1;if(a.length!==this.activeElements.length)return b=!0;k(a,function(a, +c){a!==this.activeElements[c]&&(b=!0)},this);return b}.call(this,a)||b){this.activeElements=a;this.draw();this.options.customTooltips&&this.options.customTooltips(!1);if(0<a.length)if(this.datasets&&1<this.datasets.length){for(var c,f,q=this.datasets.length-1;0<=q&&(c=this.datasets[q].points||this.datasets[q].bars||this.datasets[q].segments,f=J(c,a[0]),-1===f);q--);var m=[],g=[];c=function(a){var b=[],c,e=[],q=[],k,h,l;d.each(this.datasets,function(a){c=a.points||a.bars||a.segments;c[f]&&c[f].hasValue()&& +b.push(c[f])});d.each(b,function(a){e.push(a.x);q.push(a.y);m.push(d.template(this.options.multiTooltipTemplate,a));g.push({fill:a._saved.fillColor||a.fillColor,stroke:a._saved.strokeColor||a.strokeColor})},this);l=w(q);k=y(q);h=w(e);a=y(e);return{x:h>this.chart.width/2?h:a,y:(l+k)/2}}.call(this,f);(new e.MultiTooltip({x:c.x,y:c.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor, +fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:m,legendColors:g,legendColorBackground:this.options.multiTooltipKeyBackground,title:t(this.options.tooltipTitleTemplate,a[0]),chart:this.chart, +ctx:this.chart.ctx,custom:this.options.customTooltips})).draw()}else k(a,function(a){var b=a.tooltipPosition();(new e.Tooltip({x:Math.round(b.x),y:Math.round(b.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius, +text:t(this.options.tooltipTemplate,a),chart:this.chart,custom:this.options.customTooltips})).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}});e.Type.extend=function(a){var b=this,c=function(){return b.apply(this,arguments)};c.prototype=h(b.prototype);r(c.prototype,a);c.extend=e.Type.extend;if(a.name||b.prototype.name){var f=a.name||b.prototype.name,d=e.defaults[b.prototype.name]?h(e.defaults[b.prototype.name]):{};e.defaults[f]= +r(d,a.defaults);e.types[f]=c;e.prototype[f]=function(a,b){var d=I(e.defaults.global,e.defaults[f],b||{});return new c(a,d,this)}}else L("Name not provided for this chart, so it hasn't been registered");return b};e.Element=function(a){r(this,a);this.initialize.apply(this,arguments);this.save()};r(e.Element.prototype,{initialize:function(){},restore:function(a){a?k(a,function(a){this[a]=this._saved[a]},this):r(this,this._saved);return this},save:function(){this._saved=h(this);delete this._saved._saved; +return this},update:function(a){k(a,function(a,c){this._saved[c]=this[c];this[c]=a},this);return this},transition:function(a,b){k(a,function(a,f){this[f]=(a-this._saved[f])*b+this._saved[f]},this);return this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return u(this.value)}});e.Element.extend=D;e.Point=e.Element.extend({display:!0,inRange:function(a,b){return Math.pow(a-this.x,2)+Math.pow(b-this.y,2)<Math.pow(this.hitDetectionRadius+this.radius,2)},draw:function(){if(this.display){var a= +this.ctx;a.beginPath();a.arc(this.x,this.y,this.radius,0,2*Math.PI);a.closePath();a.strokeStyle=this.strokeColor;a.lineWidth=this.strokeWidth;a.fillStyle=this.fillColor;a.fill();a.stroke()}}});e.Arc=e.Element.extend({inRange:function(a,b){var c=d.getAngleFromPoint(this,{x:a,y:b}),f=c.angle%(2*Math.PI),e=(2*Math.PI+this.startAngle)%(2*Math.PI),m=(2*Math.PI+this.endAngle)%(2*Math.PI)||360,c=c.distance>=this.innerRadius&&c.distance<=this.outerRadius;return(m<e?f<=m||f>=e:f>=e&&f<=m)&&c},tooltipPosition:function(){var a= +this.startAngle+(this.endAngle-this.startAngle)/2,b=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(a)*b,y:this.y+Math.sin(a)*b}},draw:function(a){a=this.ctx;a.beginPath();a.arc(this.x,this.y,0>this.outerRadius?0:this.outerRadius,this.startAngle,this.endAngle);a.arc(this.x,this.y,0>this.innerRadius?0:this.innerRadius,this.endAngle,this.startAngle,!0);a.closePath();a.strokeStyle=this.strokeColor;a.lineWidth=this.strokeWidth;a.fillStyle=this.fillColor;a.fill();a.lineJoin= +"bevel";this.showStroke&&a.stroke()}});e.Rectangle=e.Element.extend({draw:function(){var a=this.ctx,b=this.width/2,c=this.x-b,b=this.x+b,f=this.base-(this.base-this.y),d=this.strokeWidth/2;this.showStroke&&(c+=d,b-=d,f+=d);a.beginPath();a.fillStyle=this.fillColor;a.strokeStyle=this.strokeColor;a.lineWidth=this.strokeWidth;a.moveTo(c,this.base);a.lineTo(c,f);a.lineTo(b,f);a.lineTo(b,this.base);a.fill();this.showStroke&&a.stroke()},height:function(){return this.base-this.y},inRange:function(a,b){return a>= +this.x-this.width/2&&a<=this.x+this.width/2&&b>=this.y&&b<=this.base}});e.Animation=e.Element.extend({currentStep:null,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null});e.Tooltip=e.Element.extend({draw:function(){var a=this.chart.ctx;a.font=v(this.fontSize,this.fontStyle,this.fontFamily);this.xAlign="center";this.yAlign="above";var b=this.caretPadding=2,c=a.measureText(this.text).width+2*this.xPadding,f=this.fontSize+2*this.yPadding,d=f+this.caretHeight+b;this.x+ +c/2>this.chart.width?this.xAlign="left":0>this.x-c/2&&(this.xAlign="right");0>this.y-d&&(this.yAlign="below");var e=this.x-c/2,d=this.y-d;a.fillStyle=this.fillColor;if(this.custom)this.custom(this);else{switch(this.yAlign){case "above":a.beginPath();a.moveTo(this.x,this.y-b);a.lineTo(this.x+this.caretHeight,this.y-(b+this.caretHeight));a.lineTo(this.x-this.caretHeight,this.y-(b+this.caretHeight));a.closePath();a.fill();break;case "below":d=this.y+b+this.caretHeight,a.beginPath(),a.moveTo(this.x,this.y+ +b),a.lineTo(this.x+this.caretHeight,this.y+b+this.caretHeight),a.lineTo(this.x-this.caretHeight,this.y+b+this.caretHeight),a.closePath(),a.fill()}switch(this.xAlign){case "left":e=this.x-c+(this.cornerRadius+this.caretHeight);break;case "right":e=this.x-(this.cornerRadius+this.caretHeight)}H(a,e,d,c,f,this.cornerRadius);a.fill();a.fillStyle=this.textColor;a.textAlign="center";a.textBaseline="middle";a.fillText(this.text,e+c/2,d+f/2)}}});e.MultiTooltip=e.Element.extend({initialize:function(){this.font= +v(this.fontSize,this.fontStyle,this.fontFamily);this.titleFont=v(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);this.titleHeight=this.title?1.5*this.titleFontSize:0;this.height=this.labels.length*this.fontSize+this.fontSize/2*(this.labels.length-1)+2*this.yPadding+this.titleHeight;this.ctx.font=this.titleFont;var a=this.ctx.measureText(this.title).width,b=C(this.ctx,this.font,this.labels)+this.fontSize+3;this.width=y([b,a])+2*this.xPadding;a=this.height/2;0>this.y-a?this.y=a:this.y+ +a>this.chart.height&&(this.y=this.chart.height-a);this.x=this.x>this.chart.width/2?this.x-(this.xOffset+this.width):this.x+this.xOffset},getLineHeight:function(a){var b=this.y-this.height/2+this.yPadding;return 0===a?b+this.titleHeight/3:b+(1.5*this.fontSize*(a-1)+this.fontSize/2)+this.titleHeight},draw:function(){if(this.custom)this.custom(this);else{H(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var a=this.ctx;a.fillStyle=this.fillColor;a.fill();a.closePath();a.textAlign= +"left";a.textBaseline="middle";a.fillStyle=this.titleTextColor;a.font=this.titleFont;a.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0));a.font=this.font;d.each(this.labels,function(b,c){a.fillStyle=this.textColor;a.fillText(b,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(c+1));a.fillStyle=this.legendColorBackground;a.fillRect(this.x+this.xPadding,this.getLineHeight(c+1)-this.fontSize/2,this.fontSize,this.fontSize);a.fillStyle=this.legendColors[c].fill;a.fillRect(this.x+this.xPadding, +this.getLineHeight(c+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}});e.Scale=e.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var a=E(this.stepValue),b=0;b<=this.steps;b++)this.yLabels.push(t(this.templateString,{value:(this.min+b*this.stepValue).toFixed(a)}));this.yLabelWidth=this.display&&this.showLabels?C(this.ctx,this.font,this.yLabels)+10:0},addXLabel:function(a){this.xLabels.push(a);this.valuesCount++;this.fit()},removeXLabel:function(){this.xLabels.shift(); +this.valuesCount--;this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0;this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height;this.startPoint+=this.padding;var a=this.endPoint-=this.padding,b=this.endPoint-this.startPoint,c;this.calculateYRange(b);this.buildYLabels();for(this.calculateXLabelRotation();b>this.endPoint-this.startPoint;)b=this.endPoint-this.startPoint,c=this.yLabelWidth,this.calculateYRange(b),this.buildYLabels(),c<this.yLabelWidth&&(this.endPoint= +a,this.calculateXLabelRotation())},calculateXLabelRotation:function(){this.ctx.font=this.font;var a=this.ctx.measureText(this.xLabels[0]).width,b;this.xScalePaddingRight=this.ctx.measureText(this.xLabels[this.xLabels.length-1]).width/2+3;this.xScalePaddingLeft=a/2>this.yLabelWidth?a/2:this.yLabelWidth;this.xLabelRotation=0;if(this.display){var c=C(this.ctx,this.font,this.xLabels),f;this.xLabelWidth=c;for(var d=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>d&&0===this.xLabelRotation|| +this.xLabelWidth>d&&90>=this.xLabelRotation&&0<this.xLabelRotation;)f=Math.cos(B(this.xLabelRotation)),b=f*a,b+this.fontSize/2>this.yLabelWidth&&(this.xScalePaddingLeft=b+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=f*c;0<this.xLabelRotation&&(this.endPoint-=Math.sin(B(this.xLabelRotation))*c+3)}else this.xLabelWidth=0,this.xScalePaddingLeft=this.xScalePaddingRight=this.padding},calculateYRange:A,drawingArea:function(){return this.startPoint-this.endPoint}, +calculateY:function(a){var b=this.drawingArea()/(this.min-this.max);return this.endPoint-b*(a-this.min)},calculateX:function(a){var b=(this.width-(this.xScalePaddingLeft+this.xScalePaddingRight))/Math.max(this.valuesCount-(this.offsetGridLines?0:1),1);a=b*a+this.xScalePaddingLeft;this.offsetGridLines&&(a+=b/2);return Math.round(a)},update:function(a){d.extend(this,a);this.fit()},draw:function(){var a=this.ctx,b=(this.endPoint-this.startPoint)/this.steps,c=Math.round(this.xScalePaddingLeft);this.display&& +(a.fillStyle=this.textColor,a.font=this.font,k(this.yLabels,function(f,e){var k=this.endPoint-b*e,g=Math.round(k),h=this.showHorizontalLines;a.textAlign="right";a.textBaseline="middle";this.showLabels&&a.fillText(f,c-10,k);0!==e||h||(h=!0);h&&a.beginPath();0<e?(a.lineWidth=this.gridLineWidth,a.strokeStyle=this.gridLineColor):(a.lineWidth=this.lineWidth,a.strokeStyle=this.lineColor);g+=d.aliasPixel(a.lineWidth);h&&(a.moveTo(c,g),a.lineTo(this.width,g),a.stroke(),a.closePath());a.lineWidth=this.lineWidth; +a.strokeStyle=this.lineColor;a.beginPath();a.moveTo(c-5,g);a.lineTo(c,g);a.stroke();a.closePath()},this),k(this.xLabels,function(b,c){var d=this.calculateX(c)+F(this.lineWidth),e=this.calculateX(c-(this.offsetGridLines?.5:0))+F(this.lineWidth),k=0<this.xLabelRotation,h=this.showVerticalLines;0!==c||h||(h=!0);h&&a.beginPath();0<c?(a.lineWidth=this.gridLineWidth,a.strokeStyle=this.gridLineColor):(a.lineWidth=this.lineWidth,a.strokeStyle=this.lineColor);h&&(a.moveTo(e,this.endPoint),a.lineTo(e,this.startPoint- +3),a.stroke(),a.closePath());a.lineWidth=this.lineWidth;a.strokeStyle=this.lineColor;a.beginPath();a.moveTo(e,this.endPoint);a.lineTo(e,this.endPoint+5);a.stroke();a.closePath();a.save();a.translate(d,k?this.endPoint+12:this.endPoint+8);a.rotate(-1*B(this.xLabelRotation));a.font=this.font;a.textAlign=k?"right":"center";a.textBaseline=k?"middle":"top";a.fillText(b,0,0);a.restore()},this))}});e.RadialScale=e.Element.extend({initialize:function(){this.size=w([this.height,this.width]);this.drawingArea= +this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(a){return this.drawingArea/(this.max-this.min)*(a-this.min)},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize();this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var a=E(this.stepValue),b=0;b<=this.steps;b++)this.yLabels.push(t(this.templateString,{value:(this.min+b*this.stepValue).toFixed(a)}))}, +getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var a=w([this.height/2-this.pointLabelFontSize-5,this.width/2]),b,c,d,e=this.width,k,g=0,h;this.ctx.font=v(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);for(c=0;c<this.valuesCount;c++)b=this.getPointPosition(c,a),d=this.ctx.measureText(t(this.templateString,{value:this.labels[c]})).width+5,0===c||c===this.valuesCount/2?(d/=2,b.x+d>e&&(e=b.x+d,k=c),b.x-d<g&&(g=b.x-d,h=c)):c<this.valuesCount/ +2?b.x+d>e&&(e=b.x+d,k=c):c>this.valuesCount/2&&b.x-d<g&&(g=b.x-d,h=c);b=g;e=Math.ceil(e-this.width);k=this.getIndexAngle(k);h=this.getIndexAngle(h);k=e/Math.sin(k+Math.PI/2);h=b/Math.sin(h+Math.PI/2);k=u(k)?k:0;h=u(h)?h:0;this.drawingArea=a-(h+k)/2;this.setCenterPoint(h,k)},setCenterPoint:function(a,b){this.xCenter=(a+this.drawingArea+(this.width-b-this.drawingArea))/2;this.yCenter=this.height/2},getIndexAngle:function(a){return 2*Math.PI/this.valuesCount*a-Math.PI/2},getPointPosition:function(a, +b){var c=this.getIndexAngle(a);return{x:Math.cos(c)*b+this.xCenter,y:Math.sin(c)*b+this.yCenter}},draw:function(){if(this.display){var a=this.ctx;k(this.yLabels,function(b,c){if(0<c){var d=this.drawingArea/this.steps*c,e=this.yCenter-d;if(0<this.lineWidth){a.strokeStyle=this.lineColor;a.lineWidth=this.lineWidth;if(this.lineArc)a.beginPath(),a.arc(this.xCenter,this.yCenter,d,0,2*Math.PI);else{a.beginPath();for(var f=0;f<this.valuesCount;f++)d=this.getPointPosition(f,this.calculateCenterOffset(this.min+ +c*this.stepValue)),0===f?a.moveTo(d.x,d.y):a.lineTo(d.x,d.y)}a.closePath();a.stroke()}this.showLabels&&(a.font=v(this.fontSize,this.fontStyle,this.fontFamily),this.showLabelBackdrop&&(d=a.measureText(b).width,a.fillStyle=this.backdropColor,a.fillRect(this.xCenter-d/2-this.backdropPaddingX,e-this.fontSize/2-this.backdropPaddingY,d+2*this.backdropPaddingX,this.fontSize+2*this.backdropPaddingY)),a.textAlign="center",a.textBaseline="middle",a.fillStyle=this.fontColor,a.fillText(b,this.xCenter,e))}},this); +if(!this.lineArc){a.lineWidth=this.angleLineWidth;a.strokeStyle=this.angleLineColor;for(var b=this.valuesCount-1;0<=b;b--){var c=null,d=null;0<this.angleLineWidth&&(c=this.calculateCenterOffset(this.max),d=this.getPointPosition(b,c),a.beginPath(),a.moveTo(this.xCenter,this.yCenter),a.lineTo(d.x,d.y),a.stroke(),a.closePath());if(this.backgroundColors&&this.backgroundColors.length==this.valuesCount){null==c&&(c=this.calculateCenterOffset(this.max));null==d&&(d=this.getPointPosition(b,c));var e=this.getPointPosition(0=== +b?this.valuesCount-1:b-1,c),h=this.getPointPosition(b===this.valuesCount-1?0:b+1,c),c=(e.x+d.x)/2,e=(e.y+d.y)/2,g=(d.x+h.x)/2,h=(d.y+h.y)/2;a.beginPath();a.moveTo(this.xCenter,this.yCenter);a.lineTo(c,e);a.lineTo(d.x,d.y);a.lineTo(g,h);a.fillStyle=this.backgroundColors[b];a.fill();a.closePath()}d=this.getPointPosition(b,this.calculateCenterOffset(this.max)+5);a.font=v(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);a.fillStyle=this.pointLabelFontColor;e=this.labels.length; +c=this.labels.length/2;g=c/2;h=b<g||b>e-g;e=b===g||b===e-g;a.textAlign=0===b?"center":b===c?"center":b<c?"left":"right";a.textBaseline=e?"middle":h?"bottom":"top";a.fillText(this.labels[b],d.x,d.y)}}}}});e.animationService={frameDuration:17,animations:[],dropFrames:0,addAnimation:function(a,b){for(var c=0;c<this.animations.length;++c)if(this.animations[c].chartInstance===a){this.animations[c].animationObject=b;return}this.animations.push({chartInstance:a,animationObject:b});1==this.animations.length&& +d.requestAnimFrame.call(window,this.digestWrapper)},cancelAnimation:function(a){var b=d.findNextWhere(this.animations,function(b){return b.chartInstance===a});b&&this.animations.splice(b,1)},digestWrapper:function(){e.animationService.startDigest.call(e.animationService)},startDigest:function(){var a=Date.now(),b=0;1<this.dropFrames&&(b=Math.floor(this.dropFrames),this.dropFrames-=b);for(var c=0;c<this.animations.length;c++)null===this.animations[c].animationObject.currentStep&&(this.animations[c].animationObject.currentStep= +0),this.animations[c].animationObject.currentStep+=1+b,this.animations[c].animationObject.currentStep>this.animations[c].animationObject.numSteps&&(this.animations[c].animationObject.currentStep=this.animations[c].animationObject.numSteps),this.animations[c].animationObject.render(this.animations[c].chartInstance,this.animations[c].animationObject),this.animations[c].animationObject.currentStep==this.animations[c].animationObject.numSteps&&(this.animations[c].animationObject.onAnimationComplete.call(this.animations[c].chartInstance), +this.animations.splice(c,1),c--);a=(Date.now()-a-this.frameDuration)/this.frameDuration;1<a&&(this.dropFrames+=a);0<this.animations.length&&d.requestAnimFrame.call(window,this.digestWrapper)}};d.addEvent(window,"resize",function(){var a;return function(){clearTimeout(a);a=setTimeout(function(){k(e.instances,function(a){a.options.responsive&&a.resize(a.render,!0)})},50)}}());M?define(function(){return e}):"object"===typeof module&&module.exports&&(module.exports=e);p.Chart=e;e.noConflict=function(){p.Chart= +l;return e}}).call(this); +(function(){var p=this.Chart,l=p.helpers,e={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'<ul class="<%=name.toLowerCase()%>-legend"><% for (var i=0; i<segments.length; i++){%><li><span style="background-color:<%=segments[i].fillColor%>"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>'};p.Type.extend({name:"Doughnut",defaults:e, +initialize:function(d){this.segments=[];this.outerRadius=(l.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2;this.SegmentArc=p.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2});this.options.showTooltips&&l.bindEvents(this,this.options.tooltipEvents,function(d){d="mouseout"!==d.type?this.getSegmentsAtEvent(d):[];l.each(this.segments,function(d){d.restore(["fillColor"])});l.each(d,function(d){d.fillColor=d.highlightColor});this.showTooltip(d)}); +this.calculateTotal(d);l.each(d,function(e,h){e.color||(e.color="hsl("+360*h/d.length+", 100%, 50%)");this.addData(e,h,!0)},this);this.render()},getSegmentsAtEvent:function(d){var e=[],h=l.getRelativePosition(d);l.each(this.segments,function(d){d.inRange(h.x,h.y)&&e.push(d)},this);return e},addData:function(d,e,h){e=void 0!==e?e:this.segments.length;"undefined"===typeof d.color&&(d.color=p.defaults.global.segmentColorDefault[e%p.defaults.global.segmentColorDefault.length],d.highlight=p.defaults.global.segmentHighlightColorDefaults[e% +p.defaults.global.segmentHighlightColorDefaults.length]);this.segments.splice(e,0,new this.SegmentArc({value:d.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:d.color,highlightColor:d.highlight||d.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate? +0:this.calculateCircumference(d.value),label:d.label}));h||(this.reflow(),this.update())},calculateCircumference:function(d){return 0<this.total?d/this.total*Math.PI*2:0},calculateTotal:function(d){this.total=0;l.each(d,function(d){this.total+=Math.abs(d.value)},this)},update:function(){this.calculateTotal(this.segments);l.each(this.activeElements,function(d){d.restore(["fillColor"])});l.each(this.segments,function(d){d.save()});this.render()},removeData:function(d){d=l.isNumber(d)?d:this.segments.length- +1;this.segments.splice(d,1);this.reflow();this.update()},reflow:function(){l.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2});this.outerRadius=(l.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2;l.each(this.segments,function(d){d.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(d){var e=d?d:1;this.clear();l.each(this.segments,function(d,l){d.transition({circumference:this.calculateCircumference(d.value), +outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},e);d.endAngle=d.startAngle+d.circumference;d.draw();0===l&&(d.startAngle=1.5*Math.PI);l<this.segments.length-1&&(this.segments[l+1].startAngle=d.endAngle)},this)}});p.types.Doughnut.extend({name:"Pie",defaults:l.merge(e,{percentageInnerCutout:0})})}).call(this); diff --git a/applications/luci-app-nlbwmon/luasrc/controller/nlbw.lua b/applications/luci-app-nlbwmon/luasrc/controller/nlbw.lua new file mode 100644 index 0000000000..bb56bc6e6e --- /dev/null +++ b/applications/luci-app-nlbwmon/luasrc/controller/nlbw.lua @@ -0,0 +1,225 @@ +-- Copyright 2017 Jo-Philipp Wich <jo@mein.io> +-- Licensed to the public under the Apache License 2.0. + +module("luci.controller.nlbw", package.seeall) + +function index() + entry({"admin", "nlbw"}, firstchild(), _("Bandwidth Monitor"), 80) + entry({"admin", "nlbw", "display"}, template("nlbw/display"), _("Display"), 1) + entry({"admin", "nlbw", "config"}, cbi("nlbw/config"), _("Configuration"), 2) + entry({"admin", "nlbw", "backup"}, template("nlbw/backup"), _("Backup"), 3) + entry({"admin", "nlbw", "data"}, call("action_data"), nil, 4) + entry({"admin", "nlbw", "list"}, call("action_list"), nil, 5) + entry({"admin", "nlbw", "ptr"}, call("action_ptr"), nil, 6).leaf = true + entry({"admin", "nlbw", "download"}, call("action_download"), nil, 7) + entry({"admin", "nlbw", "restore"}, post("action_restore"), nil, 8) + entry({"admin", "nlbw", "commit"}, call("action_commit"), nil, 9) +end + +local function exec(cmd, args, writer) + local os = require "os" + local nixio = require "nixio" + + local fdi, fdo = nixio.pipe() + local pid = nixio.fork() + + if pid > 0 then + fdo:close() + + while true do + local buffer = fdi:read(2048) + local wpid, stat, code = nixio.waitpid(pid, "nohang") + + if writer and buffer and #buffer > 0 then + writer(buffer) + end + + if wpid and stat == "exited" then + break + end + end + elseif pid == 0 then + nixio.dup(fdo, nixio.stdout) + fdi:close() + fdo:close() + nixio.exece(cmd, args, nil) + nixio.stdout:close() + os.exit(1) + end +end + +function action_data() + local http = require "luci.http" + + local types = { + csv = "text/csv", + json = "application/json" + } + + local args = { } + local mtype = http.formvalue("type") or "json" + local delim = http.formvalue("delim") or ";" + local period = http.formvalue("period") + local group_by = http.formvalue("group_by") + local order_by = http.formvalue("order_by") + + if types[mtype] then + args[#args+1] = "-c" + args[#args+1] = mtype + else + http.status(400, "Unsupported type") + return + end + + if delim and #delim > 0 then + args[#args+1] = "-s%s" % delim + end + + if period and #period > 0 then + args[#args+1] = "-t" + args[#args+1] = period + end + + if group_by and #group_by > 0 then + args[#args+1] = "-g" + args[#args+1] = group_by + end + + if order_by and #order_by > 0 then + args[#args+1] = "-o" + args[#args+1] = order_by + end + + http.prepare_content(types[mtype]) + exec("/usr/sbin/nlbw", args, http.write) +end + +function action_list() + local http = require "luci.http" + + local fd = io.popen("/usr/sbin/nlbw -c list") + local periods = { } + + if fd then + while true do + local period = fd:read("*l") + + if not period then + break + end + + periods[#periods+1] = period + end + + fd:close() + end + + http.prepare_content("application/json") + http.write_json(periods) +end + +function action_ptr(...) + local http = require "luci.http" + local util = require "luci.util" + + http.prepare_content("application/json") + http.write_json(util.ubus("network.rrdns", "lookup", { + addrs = {...}, timeout = 3000 + })) +end + +function action_download() + local nixio = require "nixio" + local http = require "luci.http" + local sys = require "luci.sys" + local uci = require "luci.model.uci".cursor() + + local dir = uci:get_first("nlbwmon", "nlbwmon", "database_directory") + or "/var/lib/nlbwmon" + + if dir and nixio.fs.stat(dir, "type") == "dir" then + local n = "nlbwmon-backup-%s-%s.tar.gz" + %{ sys.hostname(), os.date("%Y-%m-%d") } + + http.prepare_content("application/octet-stream") + http.header("Content-Disposition", "attachment; filename=\"%s\"" % n) + exec("/bin/tar", { "-C", dir, "-c", "-z", ".", "-f", "-" }, http.write) + else + http.status(500, "Unable to find database directory") + end +end + +function action_restore() + local nixio = require "nixio" + local http = require "luci.http" + local i18n = require "luci.i18n" + local tpl = require "luci.template" + local uci = require "luci.model.uci".cursor() + + local tmp = "/tmp/nlbw-restore.tar.gz" + local dir = uci:get_first("nlbwmon", "nlbwmon", "database_directory") + or "/var/lib/nlbwmon" + + local fp + http.setfilehandler( + function(meta, chunk, eof) + if not fp and meta and meta.name == "archive" then + fp = io.open(tmp, "w") + end + if fp and chunk then + fp:write(chunk) + end + if fp and eof then + fp:close() + end + end) + + local files = { } + local tar = io.popen("/bin/tar -tzf %s" % tmp, "r") + if tar then + while true do + local file = tar:read("*l") + if not file then + break + elseif file:match("^%d%d%d%d%d%d%d%d%.db%.gz$") or + file:match("^%./%d%d%d%d%d%d%d%d%.db%.gz$") then + files[#files+1] = file + end + end + tar:close() + end + + if #files == 0 then + http.status(500, "Internal Server Error") + tpl.render("nlbw/backup", { + message = i18n.translate("Invalid or empty backup archive") + }) + return + end + + + local output = { } + + exec("/etc/init.d/nlbwmon", { "stop" }) + exec("/bin/mkdir", { "-p", dir }) + + exec("/bin/tar", { "-C", dir, "-vxzf", tmp, unpack(files) }, + function(chunk) output[#output+1] = chunk:match("%S+") end) + + exec("/bin/rm", { "-f", tmp }) + exec("/etc/init.d/nlbwmon", { "start" }) + + tpl.render("nlbw/backup", { + message = i18n.translatef( + "The following database files have been restored: %s", + table.concat(output, ", ")) + }) +end + +function action_commit() + local http = require "luci.http" + local disp = require "luci.dispatcher" + + http.redirect(disp.build_url("admin/nlbw/display")) + exec("/usr/sbin/nlbw", { "-c", "commit" }) +end diff --git a/applications/luci-app-nlbwmon/luasrc/model/cbi/nlbw/config.lua b/applications/luci-app-nlbwmon/luasrc/model/cbi/nlbw/config.lua new file mode 100644 index 0000000000..71e096c617 --- /dev/null +++ b/applications/luci-app-nlbwmon/luasrc/model/cbi/nlbw/config.lua @@ -0,0 +1,215 @@ +-- Copyright 2017 Jo-Philipp Wich <jo@mein.io> +-- Licensed to the public under the Apache License 2.0. + +local utl = require "luci.util" +local sys = require "luci.sys" +local fs = require "nixio.fs" +local ip = require "luci.ip" +local nw = require "luci.model.network" + +local s, m, period, warning, date, days, interval, ifaces, subnets, limit, prealloc, compress, generations, commit, refresh, directory, protocols + +m = Map("nlbwmon", translate("Netlink Bandwidth Monitor - Configuration"), + translate("The Netlink Bandwidth Monitor (nlbwmon) is a lightweight, efficient traffic accounting program keeping track of bandwidth usage per host and protocol.")) + +nw.init(luci.model.uci.cursor_state()) + +s = m:section(TypedSection, "nlbwmon") +s.anonymous = true +s.addremove = false +s:tab("general", translate("General Settings")) +s:tab("advanced", translate("Advanced Settings")) +s:tab("protocol", translate("Protocol Mapping"), + translate("Protocol mappings to distinguish traffic types per host, one mapping per line. The first value specifies the IP protocol, the second value the port number and the third column is the name of the mapped protocol.")) + +period = s:taboption("general", ListValue, "_period", translate("Accounting period"), + translate("Choose \"Day of month\" to restart the accounting period monthly on a specific date, e.g. every 3rd. Choose \"Fixed interval\" to restart the accounting period exactly every N days, beginning at a given date.")) + +period:value("relative", translate("Day of month")) +period:value("absolute", translate("Fixed interval")) + +period.write = function(self, cfg, val) + if period:formvalue(cfg) == "relative" then + m:set(cfg, "database_interval", interval:formvalue(cfg)) + else + m:set(cfg, "database_interval", "%s/%s" %{ + date:formvalue(cfg), + days:formvalue(cfg) + }) + end +end + +period.cfgvalue = function(self, cfg) + local val = m:get(cfg, "database_interval") or "" + if val:match("^%d%d%d%d%-%d%d%-%d%d/%d+$") then + return "absolute" + end + return "relative" +end + + +warning = s:taboption("general", DummyValue, "_warning", translate("Warning")) +warning.default = translatef("Changing the accounting interval type will invalidate existing databases!<br /><strong><a href=\"%s\">Download backup</a></strong>.", luci.dispatcher.build_url("admin/nlbw/backup")) +warning.rawhtml = true + +if (m.uci:get_first("nlbwmon", "nlbwmon", "database_interval") or ""):match("^%d%d%d%d-%d%d-%d%d/%d+$") then + warning:depends("_period", "relative") +else + warning:depends("_period", "absolute") +end + + +interval = s:taboption("general", Value, "_interval", translate("Due date"), + translate("Day of month to restart the accounting period. Use negative values to count towards the end of month, e.g. \"-5\" to specify the 27th of July or the 24th of Februrary.")) + +interval.datatype = "or(range(1,31),range(-31,-1))" +interval.placeholder = "1" +interval:value("1", translate("1 - Restart every 1st of month")) +interval:value("-1", translate("-1 - Restart every last day of month")) +interval:value("-7", translate("-7 - Restart a week before end of month")) +interval.rmempty = false +interval:depends("_period", "relative") +interval.write = period.write + +interval.cfgvalue = function(self, cfg) + local val = m:get(cfg, "database_interval") + return val and tonumber(val) +end + + +date = s:taboption("general", Value, "_date", translate("Start date"), + translate("Start date of the first accounting period, e.g. begin of ISP contract.")) + +date.datatype = "dateyyyymmdd" +date.placeholder = "2016-03-15" +date.rmempty = false +date:depends("_period", "absolute") +date.write = period.write + +date.cfgvalue = function(self, cfg) + local val = m:get(cfg, "database_interval") or "" + return (val:match("^(%d%d%d%d%-%d%d%-%d%d)/%d+$")) +end + + +days = s:taboption("general", Value, "_days", translate("Interval"), + translate("Length of accounting interval in days.")) + +days.datatype = "min(1)" +days.placeholder = "30" +days.rmempty = false +days:depends("_period", "absolute") +days.write = period.write + +days.cfgvalue = function(self, cfg) + local val = m:get(cfg, "database_interval") or "" + return (val:match("^%d%d%d%d%-%d%d%-%d%d/(%d+)$")) +end + + +ifaces = s:taboption("general", Value, "_ifaces", translate("Local interfaces"), + translate("Only conntrack streams from or to any of these networks are counted.")) + +ifaces.template = "cbi/network_netlist" +ifaces.widget = "checkbox" +ifaces.nocreate = true + +ifaces.cfgvalue = function(self, cfg) + return m:get(cfg, "local_network") +end + +ifaces.write = function(self, cfg) + local item + local items = {} + for item in utl.imatch(subnets:formvalue(cfg)) do + items[#items+1] = item + end + for item in utl.imatch(ifaces:formvalue(cfg)) do + items[#items+1] = item + end + m:set(cfg, "local_network", items) +end + + +subnets = s:taboption("general", DynamicList, "_subnets", translate("Local subnets"), + translate("Only conntrack streams from or to any of these subnets are counted.")) + +subnets.datatype = "ipaddr" + +subnets.cfgvalue = function(self, cfg) + local subnet + local subnets = {} + for subnet in utl.imatch(m:get(cfg, "local_network")) do + subnet = ip.new(subnet) + subnets[#subnets+1] = subnet and subnet:string() + end + return subnets +end + +subnets.write = ifaces.write + + +limit = s:taboption("advanced", Value, "database_limit", translate("Maximum entries"), + translate("The maximum amount of entries that should be put into the database, setting the limit to 0 will allow databases to grow indefinitely.")) + +limit.datatype = "uinteger" +limit.placeholder = "10000" + +prealloc = s:taboption("advanced", Flag, "database_prealloc", translate("Preallocate database"), + translate("Whether to preallocate the maximum possible database size in memory. This is mainly useful for memory constrained systems which might not be able to satisfy memory allocation after longer uptime periods.")) + +prealloc:depends({["database_limit"] = "0", ["!reverse"] = true }) + + +compress = s:taboption("advanced", Flag, "database_compress", translate("Compress database"), + translate("Whether to gzip compress archive databases. Compressing the database files makes accessing old data slightly slower but helps to reduce storage requirements.")) + +compress.default = compress.enabled + + +generations = s:taboption("advanced", Value, "database_generations", translate("Stored periods"), + translate("Maximum number of accounting periods to keep, use zero to keep databases forever.")) + +generations.datatype = "uinteger" +generations.placeholder = "10" + + +commit = s:taboption("advanced", Value, "commit_interval", translate("Commit interval"), + translate("Interval at which the temporary in-memory database is committed to the persistent database directory.")) + +commit.placeholder = "24h" +commit:value("24h", translate("24h - least flash wear at the expense of data loss risk")) +commit:value("12h", translate("12h - compromise between risk of data loss and flash wear")) +commit:value("10m", translate("10m - frequent commits at the expense of flash wear")) +commit:value("60s", translate("60s - commit minutely, useful for non-flash storage")) + + +refresh = s:taboption("advanced", Value, "refresh_interval", translate("Refresh interval"), + translate("Interval at which traffic counters of still established connections are refreshed from netlink information.")) + +refresh.placeholder = "30s" +refresh:value("30s", translate("30s - refresh twice per minute for reasonably current stats")) +refresh:value("5m", translate("5m - rarely refresh to avoid frequently clearing conntrack counters")) + + +directory = s:taboption("advanced", Value, "database_directory", translate("Database directory"), + translate("Database storage directory. One file per accounting period will be placed into this directory.")) + +directory.placeholder = "/var/lib/nlbwmon" + + +protocols = s:taboption("protocol", TextValue, "_protocols") +protocols.rows = 50 + +protocols.cfgvalue = function(self, cfg) + return fs.readfile("/usr/share/nlbwmon/protocols") +end + +protocols.write = function(self, cfg, value) + fs.writefile("/usr/share/nlbwmon/protocols", (value or ""):gsub("\r\n", "\n")) +end + +protocols.remove = protocols.write + + +return m diff --git a/applications/luci-app-nlbwmon/luasrc/view/nlbw/backup.htm b/applications/luci-app-nlbwmon/luasrc/view/nlbw/backup.htm new file mode 100644 index 0000000000..ea2e0f05cf --- /dev/null +++ b/applications/luci-app-nlbwmon/luasrc/view/nlbw/backup.htm @@ -0,0 +1,34 @@ +<%# + Copyright 2017 Jo-Philipp Wich <jo@mein.io> + Licensed to the public under the Apache License 2.0. +-%> + +<%+header%> + +<script type="text/javascript" src="<%=resource%>/cbi.js"></script> + +<h2 name="content"><%:Netlink Bandwidth Monitor - Backup / Restore %></h2> + +<fieldset class="cbi-section"> + <legend><%:Restore Database Backup%></legend> + <p> + <form method="POST" action="<%=url("admin/nlbw/restore")%>" enctype="multipart/form-data"> + <input type="hidden" name="token" value="<%=token%>" /> + <input type="file" name="archive" accept="application/gzip,.gz" /> + <input type="submit" value="<%:Restore%>" class="cbi-button cbi-button-apply" /> + </form> + + <% if message then %> + <div class="alert-message"><%=message%></div> + <% end %> + </p> + + <legend><%:Download Database Backup%></legend> + <p> + <form method="GET" action="<%=url("admin/nlbw/download")%>"> + <input type="submit" value="<%:Generate Backup%>" class="cbi-button cbi-button-link" /> + </form> + </p> +</fieldset> + +<%+footer%> diff --git a/applications/luci-app-nlbwmon/luasrc/view/nlbw/display.htm b/applications/luci-app-nlbwmon/luasrc/view/nlbw/display.htm new file mode 100644 index 0000000000..7e85acefb3 --- /dev/null +++ b/applications/luci-app-nlbwmon/luasrc/view/nlbw/display.htm @@ -0,0 +1,1052 @@ +<%# + Copyright 2017 Jo-Philipp Wich <jo@mein.io> + Licensed to the public under the Apache License 2.0. +-%> + +<% css = [[ + + #chartjs-tooltip { + opacity: 0; + position: absolute; + background: rgba(0, 0, 0, .7); + color: white; + padding: 3px; + border-radius: 3px; + -webkit-transition: all .1s ease; + transition: all .1s ease; + pointer-events: none; + -webkit-transform: translate(-50%, 0); + transform: translate(-50%, 0); + z-index: 200; + } + + #chartjs-tooltip.above { + -webkit-transform: translate(-50%, -100%); + transform: translate(-50%, -100%); + } + + #chartjs-tooltip.above:before { + border: solid; + border-color: #111 transparent; + border-color: rgba(0, 0, 0, .8) transparent; + border-width: 8px 8px 0 8px; + bottom: 1em; + content: ""; + display: block; + left: 50%; + top: 100%; + position: absolute; + z-index: 99; + -webkit-transform: translate(-50%, 0); + transform: translate(-50%, 0); + } + + table { + border: 1px solid #999; + border-collapse: collapse; + margin: 0 0 2px !important; + } + + th, td, table table td { + border: 1px solid #999; + text-align: right; + padding: 1px 3px !important; + white-space: nowrap; + } + + tbody td { + border-bottom-color: #ccc; + } + + tbody td[rowspan] { + border-bottom-color: #999; + } + + tbody tr:last-child td { + border-bottom-color: #999; + } + + + .pie { + width: 200px; + display: inline-block; + margin: 20px; + } + + .pie label { + font-weight: bold; + font-size: 14px; + display: block; + margin-bottom: 10px; + text-align: center; + } + + .kpi { + display: inline-block; + margin: 80px 20px 20px; + vertical-align: top; + } + + .kpi ul { + list-style: none; + } + + .kpi li { + margin: 10px; + display: none; + } + + .kpi big { + font-weight: bold; + } + + #detail-bubble { + position: absolute; + opacity: 0; + visibility: hidden; + } + + #detail-bubble.in { + opacity: 1; + visibility: visible; + transition: opacity 0.5s; + } + + #detail-bubble > div { + border: 1px solid #ccc; + border-radius: 2px; + padding: 5px; + background: #fcfcfc; + } + + #detail-bubble .head { + text-align: center; + white-space: nowrap; + position: relative; + } + + #detail-bubble .head .dismiss { + top: 0; + right: 0; + width: 20px; + line-height: 20px; + text-align: center; + text-decoration: none; + font-weight: bold; + color: #000; + position: absolute; + font-size: 20px; + } + + #detail-bubble .pie { + width: 100px; + margin: 5px; + } + + #detail-bubble .kpi { + margin: 40px 5px 5px; + font-size: smaller; + text-align: left; + } + + #detail-bubble .kpi ul { + margin: 0; + } + + #bubble-arrow { + border: 1px solid #ccc; + border-width: 1px 0 0 1px; + background: #fcfcfc; + width: 15px; + height: 15px; + position: absolute; + left: 0; + top: -8px; + transform: rotate(45deg); + margin: 0 0 0 -8px; + } + + tr.active > td { + border-bottom: 2px solid red; + } + + tr.active > td.active { + border: 2px solid red; + border-bottom: none; + } + + td.detail { + border: 2px solid red; + border-top: none; + opacity: 0; + transition: opacity 0.5s; + } + + td.detail.in { + opacity: 1; + } + + th.hostname, + td.hostname { + text-align: left; + } + +]] -%> + +<%+header%> + +<script type="text/javascript" src="<%=resource%>/cbi.js"></script> +<script type="text/javascript" src="<%=resource%>/nlbw.chart.min.js"></script> +<script type="text/javascript">//<![CDATA[ + +var chartRegistry = {}, + trafficPeriods = [], + trafficData = { columns: [], data: [] }, + hostNames = {}, + hostInfo = <%=luci.util.serialize_json(luci.sys.net.host_hints())%>, + ouiData = []; + + +function off(elem) +{ + var val = [0, 0]; + do { + if (!isNaN(elem.offsetLeft) && !isNaN(elem.offsetTop)) { + val[0] += elem.offsetLeft; + val[1] += elem.offsetTop; + } + } + while ((elem = elem.offsetParent) != null); + return val; +} + +Chart.defaults.global.customTooltips = function(tooltip) { + var tooltipEl = document.getElementById('chartjs-tooltip'); + + if (!tooltipEl) { + tooltipEl = document.createElement('div'); + tooltipEl.setAttribute('id', 'chartjs-tooltip'); + document.body.appendChild(tooltipEl); + } + + if (!tooltip) { + if (tooltipEl.row) + tooltipEl.row.style.backgroundColor = ''; + + tooltipEl.style.opacity = 0; + return; + } + + var pos = off(tooltip.chart.canvas); + + tooltipEl.className = tooltip.yAlign; + tooltipEl.innerHTML = tooltip.text[0]; + + tooltipEl.style.opacity = 1; + tooltipEl.style.left = pos[0] + tooltip.x + 'px'; + tooltipEl.style.top = pos[1] + tooltip.y - tooltip.caretHeight - tooltip.caretPadding + 'px'; + + var row = tooltip.text[1], + hue = tooltip.text[2]; + + if (row && !isNaN(hue)) { + row.style.backgroundColor = 'hsl(%u, 100%%, 80%%)'.format(hue); + tooltipEl.row = row; + } +}; + +Chart.defaults.global.tooltipFontSize = 10; +Chart.defaults.global.tooltipTemplate = function(tip) { + tip.label[0] = tip.label[0].format(tip.value); + return tip.label; +}; + +function kpi(id, val1, val2, val3) +{ + var e = document.getElementById(id); + + if (val1 && val2 && val3) + e.innerHTML = '<%:%s, %s and %s%>'.format(val1, val2, val3); + else if (val1 && val2) + e.innerHTML = '<%:%s and %s%>'.format(val1, val2); + else if (val1) + e.innerHTML = val1; + + e.parentNode.style.display = val1 ? 'list-item' : ''; +} + +function pie(id, data) +{ + data.sort(function(a, b) { return b.value - a.value }); + + if (data.length === 0 || (data.length === 1 && data[0].value === 0)) + data[0] = { + value: 1, + color: '#cccccc', + label: [ '<%:no traffic%>' ] + }; + + for (var i = 0; i < data.length; i++) { + if (!data[i].color) { + var hue = 120 / (data.length-1) * i; + data[i].color = 'hsl(%u, 80%%, 50%%)'.format(hue); + data[i].label.push(hue); + } + } + + var ctx = document.getElementById(id).getContext('2d'); + + if (chartRegistry.hasOwnProperty(id)) + chartRegistry[id].destroy(); + + chartRegistry[id] = new Chart(ctx).Doughnut(data, { + segmentStrokeWidth: 1, + percentageInnerCutout: 30 + }); + + return chartRegistry[id]; +} + +function query(filter, group, order) +{ + var keys = [], columns = {}, records = {}, result = []; + + if (typeof(group) !== 'function' && typeof(group) !== 'object') + group = ['mac']; + + for (var i = 0; i < trafficData.columns.length; i++) + columns[trafficData.columns[i]] = i; + + for (var i = 0; i < trafficData.data.length; i++) { + var record = trafficData.data[i]; + + if (typeof(filter) === 'function' && filter(columns, record) !== true) + continue; + + var key; + + if (typeof(group) === 'function') { + key = group(columns, record); + } + else { + key = []; + + for (var j = 0; j < group.length; j++) + if (columns.hasOwnProperty(group[j])) + key.push(record[columns[group[j]]]); + + key = key.join(','); + } + + if (!records.hasOwnProperty(key)) { + var rec = {}; + + for (var col in columns) + rec[col] = record[columns[col]]; + + records[key] = rec; + result.push(rec); + } + else { + records[key].conns += record[columns.conns]; + records[key].rx_bytes += record[columns.rx_bytes]; + records[key].rx_pkts += record[columns.rx_pkts]; + records[key].tx_bytes += record[columns.tx_bytes]; + records[key].tx_pkts += record[columns.tx_pkts]; + } + } + + if (typeof(order) === 'function') + result.sort(order); + + return result; +} + +function oui(mac) { + var m, l = 0, r = ouiData.length / 3 - 1; + var mac1 = parseInt(mac.replace(/[^a-fA-F0-9]/g, ''), 16); + + while (l <= r) { + m = l + Math.floor((r - l) / 2); + + var mask = (0xffffffffffff - + (Math.pow(2, 48 - ouiData[m * 3 + 1]) - 1)); + + var mac1_hi = ((mac1 / 0x10000) & (mask / 0x10000)) >>> 0; + var mac1_lo = ((mac1 & 0xffff) & (mask & 0xffff)) >>> 0; + + var mac2 = parseInt(ouiData[m * 3], 16); + var mac2_hi = (mac2 / 0x10000) >>> 0; + var mac2_lo = (mac2 & 0xffff) >>> 0; + + if (mac1_hi === mac2_hi && mac1_lo === mac2_lo) + return ouiData[m * 3 + 2]; + + if (mac2_hi > mac1_hi || + (mac2_hi === mac1_hi && mac2_lo > mac1_lo)) + r = m - 1; + else + l = m + 1; + } + + return null; +} + + +function fetchData(period) +{ + XHR.get('<%=url("admin/nlbw/data")%>', { period: period, group_by: 'family,mac,ip,layer7', order_by: '-rx_bytes,-tx_bytes' }, function(xhr, res) { + if (res !== null && typeof(res) === 'object' && typeof(res.columns) === 'object' && typeof(res.data) === 'object') + trafficData = res; + + var addrs = query(null, ['ip'], null); + var ipAddrs = []; + + for (var i = 0; i < addrs.length; i++) + if (ipAddrs.indexOf(addrs[i].ip) < 0) + ipAddrs.push(addrs[i].ip); + + renderHostData(); + renderLayer7Data(); + renderIPv6Data(); + + XHR.get('<%=url("admin/nlbw/ptr")%>/' + ipAddrs.join('/'), null, function(xhr, res) { + if (res !== null && typeof(res) === 'object') + hostNames = res; + }); + }); +} + +function switchTab(tab) +{ + bubbleDismiss(); + + return cbi_t_switch('nlbw', tab); +} + +function renderPeriods() +{ + var sel = document.getElementById('nlbw.period'); + + for (var e, i = trafficPeriods.length - 1; e = trafficPeriods[i]; i--) { + var d1 = new Date(e); + var d2, pd; + + if (i) { + d2 = new Date(trafficPeriods[i - 1]); + d2.setDate(d2.getDate() - 1); + pd = '%04d-%02d-%02d'.format(d1.getFullYear(), d1.getMonth() + 1, d1.getDate()); + } + else { + d2 = new Date(); + pd = ''; + } + + var opt = document.createElement('option'); + opt.setAttribute('data-duration', (d2.getTime() - d1.getTime()) / 1000); + opt.value = pd; + opt.text = '%04d-%02d-%02d - %04d-%02d-%02d'.format( + d1.getFullYear(), d1.getMonth() + 1, d1.getDate(), + d2.getFullYear(), d2.getMonth() + 1, d2.getDate()); + + sel.appendChild(opt); + } + + sel.selectedIndex = sel.childNodes.length - 1; + sel.style.display = ''; + + sel.onchange = function() { + bubbleDismiss(); + fetchData(sel.options[sel.selectedIndex].value); + } +} + +function renderHostDetail() +{ + var key = this.getAttribute('href').substr(1), + col = this.getAttribute('data-col'), + label = this.getAttribute('data-label'), + bubble = document.getElementById('detail-bubble'), + arrow = document.getElementById('bubble-arrow'), + table = document.getElementById('bubble-table'); + + bubbleDismiss(); + + var detailData = query( + function(c, r) { + return ((r[c.mac] === key || r[c.ip] === key) && + (r[c.rx_bytes] > 0 || r[c.tx_bytes] > 0)); + }, + [col], + function(r1, r2) { + return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes)); + } + ); + + var rxData = [], txData = []; + + table.innerHTML = '<tr>' + + '<th>%s</th>'.format(label || col) + + '<th><%:Conn.%></th>' + + '<th colspan="2"><%:Down. (Bytes / Pkts.)%></th>' + + '<th colspan="2"><%:Up. (Bytes / Pkts.)%></th>' + + '</tr>'; + + for (var i = 0; i < detailData.length; i++) { + var rec = detailData[i], + row = table.insertRow(-1); + + row.insertCell(-1).innerHTML = rec[col] || '<%:other%>'; + row.insertCell(-1).innerHTML = "%1000.2m".format(rec.conns); + row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.rx_bytes); + row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.rx_pkts); + row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.tx_bytes); + row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.tx_pkts); + + rxData.push({ + label: ['%s: %%1024.2mB'.format(rec[col] || '<%:other%>'), row], + value: rec.rx_bytes + }); + + txData.push({ + label: ['%s: %%1024.2mB'.format(rec[col] || '<%:other%>'), row], + value: rec.tx_bytes + }); + } + + pie('bubble-pie1', rxData); + pie('bubble-pie2', txData); + + var mac = key.toUpperCase(); + var name = hostInfo.hasOwnProperty(mac) ? hostInfo[mac].name : null; + + if (!name) + for (var i = 0; i < detailData.length; i++) + if ((name = hostNames[detailData[i].ip]) !== undefined) + break; + + if (mac !== '00:00:00:00:00:00') { + kpi('bubble-hostname', name); + kpi('bubble-vendor', oui(mac)); + } + else { + kpi('bubble-hostname'); + kpi('bubble-vendor'); + } + + var tr = this.parentNode.parentNode, + xy = off(tr), + xy2 = off(this); + + bubble.style.width = tr.offsetWidth + 'px'; + bubble.style.left = xy[0] + 'px'; + bubble.style.top = (xy[1] + tr.offsetHeight) + 'px'; + arrow.style.left = Math.floor(xy2[0] + this.offsetWidth / 2 - xy[0]) + 'px'; + + bubble.className = 'in'; + + return false; +} + +function formatHostname(dns) +{ + if (dns === undefined || dns === null || dns === '') + return '-'; + + dns = dns.split('.')[0]; + + if (dns.length > 12) + return '<span title="%q">%h…</span>'.format(dns, dns.substr(0, 12)); + + return '%h'.format(dns); +} + +function renderHostData() +{ + var trafData = [], connData = []; + var rx_total = 0, tx_total = 0, conn_total = 0; + var table = document.getElementById('host-data'); + + var hostData = query( + function(c, r) { + return (r[c.rx_bytes] > 0 || r[c.tx_bytes] > 0); + }, + ['mac'], + //function(c, r) { + // return (r[c.mac] !== '00:00:00:00:00:00') ? r[c.mac] : r[c.ip]; + //}, + function(r1, r2) { + return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes)); + } + ); + + while (table.rows.length > 1) + table.deleteRow(1); + + for (var i = 0; i < hostData.length; i++) { + var row = table.insertRow(-1), + cell = row.insertCell(-1), + rec = hostData[i], + mac = rec.mac.toUpperCase(), + key = (mac !== '00:00:00:00:00:00') ? mac : rec.ip, + dns = hostInfo[mac] ? hostInfo[mac].name : null; + + var link1 = document.createElement('a'); + link1.onclick = renderHostDetail; + link1.href = '#' + rec.mac; + link1.setAttribute('data-col', 'ip'); + link1.setAttribute('data-label', '<%:Source IP%>'); + link1.innerHTML = (mac !== '00:00:00:00:00:00') ? mac : '<%:other%>'; + + var link2 = document.createElement('a'); + link2.onclick = renderHostDetail; + link2.href = '#' + rec.mac; + link2.setAttribute('data-col', 'layer7'); + link2.setAttribute('data-label', '<%:Protocol%>'); + link2.innerHTML = "%1000.2m".format(rec.conns); + + cell.innerHTML = formatHostname(dns); + cell.className = 'hostname'; + + row.insertCell(-1).appendChild(link1); + row.insertCell(-1).appendChild(link2); + row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.rx_bytes); + row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.rx_pkts); + row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.tx_bytes); + row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.tx_pkts); + + trafData.push({ + value: rec.rx_bytes + rec.tx_bytes, + label: ["%s: %%.2mB".format(key), row] + }); + + connData.push({ + value: rec.conns, + label: ["%s: %%.2m".format(key), row] + }); + + rx_total += rec.rx_bytes; + tx_total += rec.tx_bytes; + conn_total += rec.conns; + } + + if (table.rows.length === 1) { + var cell = table.insertRow(-1).insertCell(-1); + + cell.setAttribute('colspan', 6); + cell.innerHTML = '<em><%:No data recorded yet.%> <a href="<%=url("admin/nlbw/commit")%>"><%:Force reload…%></a></em>'; + } + + pie('traf-pie', trafData); + pie('conn-pie', connData); + + kpi('rx-total', '%1024.2mB'.format(rx_total)); + kpi('tx-total', '%1024.2mB'.format(tx_total)); + kpi('conn-total', '%1000m'.format(conn_total)); + kpi('host-total', '%u'.format(hostData.length)); +} + +function renderLayer7Data() +{ + var rxData = [], txData = []; + var topConn = [[0],[0],[0]], topRx = [[0],[0],[0]], topTx = [[0],[0],[0]]; + var table = document.getElementById('layer7-data'); + + var layer7Data = query( + null, ['layer7'], + function(r1, r2) { + return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes)); + } + ); + + while (table.rows.length > 1) + table.deleteRow(1); + + for (var i = 0, c = 0; i < layer7Data.length; i++) { + var rec = layer7Data[i], + row = table.insertRow(-1); + + rxData.push({ + value: rec.rx_bytes, + label: ["%s: %%.2mB".format(rec.layer7 || '<%:other%>'), row] + }); + + txData.push({ + value: rec.tx_bytes, + label: ["%s: %%.2mB".format(rec.layer7 || '<%:other%>'), row] + }); + + row.insertCell(-1).innerHTML = rec.layer7 || '<%:other%>'; + row.insertCell(-1).innerHTML = "%1000m".format(rec.conns); + row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.rx_bytes); + row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.rx_pkts); + row.insertCell(-1).innerHTML = "%1024.2mB".format(rec.tx_bytes); + row.insertCell(-1).innerHTML = "%1000.2mP".format(rec.tx_pkts); + + if (rec.layer7) { + topRx.push([rec.rx_bytes, rec.layer7]); + topTx.push([rec.tx_bytes, rec.layer7]); + topConn.push([rec.conns, rec.layer7]); + } + } + + if (table.rows.length === 1) { + var cell = table.insertRow(-1).insertCell(-1); + + cell.setAttribute('colspan', 6); + cell.innerHTML = '<em><%:No data recorded yet.%> <a href="<%=url("admin/nlbw/commit")%>"><%:Force reload…%></a></em>'; + } + + pie('layer7-rx-pie', rxData); + pie('layer7-tx-pie', txData); + + topRx.sort(function(a, b) { return b[0] - a[0] }); + topTx.sort(function(a, b) { return b[0] - a[0] }); + topConn.sort(function(a, b) { return b[0] - a[0] }); + + kpi('layer7-total', layer7Data.length); + kpi('layer7-most-rx', topRx[0][1], topRx[1][1], topRx[2][1]); + kpi('layer7-most-tx', topTx[0][1], topTx[1][1], topTx[2][1]); + kpi('layer7-most-conn', topConn[0][1], topConn[1][1], topConn[2][1]); +} + +function renderIPv6Data() +{ + var table = document.getElementById('ipv6-data'), + col = { }, + rx4_total = 0, + tx4_total = 0, + rx6_total = 0, + tx6_total = 0, + v4_total = 0, + v6_total = 0, + ds_total = 0, + families = { }, + records = { }; + + ipv6Data = query( + null, ['family', 'mac'], + function(r1, r2) { + return ((r2.rx_bytes + r2.tx_bytes) - (r1.rx_bytes + r1.tx_bytes)); + } + ); + + for (var i = 0, c = 0; i < ipv6Data.length; i++) { + var rec = ipv6Data[i], + mac = rec.mac.toUpperCase(), + ip = rec.ip, + fam = families[mac] || 0, + recs = records[mac] || {}; + + if (rec.family == 4) { + rx4_total += rec.rx_bytes; + tx4_total += rec.tx_bytes; + fam |= 1; + } + else { + rx6_total += rec.rx_bytes; + tx6_total += rec.tx_bytes; + fam |= 2; + } + + recs[rec.family] = rec; + records[mac] = recs; + + families[mac] = fam; + } + + for (var mac in families) { + switch (families[mac]) + { + case 3: + ds_total++; + break; + + case 2: + v6_total++; + break; + + case 1: + v4_total++; + break; + } + } + + while (table.rows.length > 1) + table.deleteRow(1); + + for (var mac in records) { + if (mac === '00:00:00:00:00:00') + continue; + + var row = table.insertRow(-1), + cell1 = row.insertCell(-1), + cell2 = row.insertCell(-1), + dns = hostInfo[mac] ? hostInfo[mac].name : null, + rec4 = records[mac][4], + rec6 = records[mac][6]; + + cell1.setAttribute('rowspan', 2); + cell1.innerHTML = formatHostname(dns); + cell1.className = 'hostname'; + + cell2.setAttribute('rowspan', 2); + cell2.innerHTML = mac; + + row.insertCell(-1).innerHTML = 'IPv4'; + row.insertCell(-1).innerHTML = rec4 ? "%1024.2mB".format(rec4.rx_bytes) : '-'; + row.insertCell(-1).innerHTML = rec4 ? "%1000.2mP".format(rec4.rx_pkts) : '-'; + row.insertCell(-1).innerHTML = rec4 ? "%1024.2mB".format(rec4.tx_bytes) : '-'; + row.insertCell(-1).innerHTML = rec4 ? "%1000.2mP".format(rec4.tx_pkts) : '-'; + + row = table.insertRow(-1); + + row.insertCell(-1).innerHTML = 'IPv6'; + row.insertCell(-1).innerHTML = rec6 ? "%1024.2mB".format(rec6.rx_bytes) : '-'; + row.insertCell(-1).innerHTML = rec6 ? "%1000.2mP".format(rec6.rx_pkts) : '-'; + row.insertCell(-1).innerHTML = rec6 ? "%1024.2mB".format(rec6.tx_bytes) : '-'; + row.insertCell(-1).innerHTML = rec6 ? "%1000.2mP".format(rec6.tx_pkts) : '-'; + } + + if (table.rows.length === 1) { + var cell = table.insertRow(-1).insertCell(-1); + + cell.setAttribute('colspan', 7); + cell.innerHTML = '<em><%:No data recorded yet.%> <a href="<%=url("admin/nlbw/commit")%>"><%:Force reload…%></a></em>'; + } + + var shareData = [], hostsData = []; + + if (rx4_total > 0 || tx4_total > 0) + shareData.push({ + value: rx4_total + tx4_total, + label: ["IPv4: %.2mB"], + color: 'hsl(140, 100%, 50%)' + }); + + if (rx6_total > 0 || tx6_total > 0) + shareData.push({ + value: rx6_total + tx6_total, + label: ["IPv6: %.2mB"], + color: 'hsl(180, 100%, 50%)' + }); + + if (v4_total > 0) + hostsData.push({ + value: v4_total, + label: ["<%:%d IPv4-only hosts%>"], + color: 'hsl(140, 100%, 50%)' + }); + + if (v6_total > 0) + hostsData.push({ + value: v6_total, + label: ["<%:%d IPv6-only hosts%>"], + color: 'hsl(180, 100%, 50%)' + }); + + if (ds_total > 0) + hostsData.push({ + value: ds_total, + label: ["<%:%d dual-stack hosts%>"], + color: 'hsl(50, 100%, 50%)' + }); + + pie('ipv6-share-pie', shareData); + pie('ipv6-hosts-pie', hostsData); + + kpi('ipv6-hosts', '%.2f%%'.format(100 / (ds_total + v4_total + v6_total) * (ds_total + v6_total))); + kpi('ipv6-share', '%.2f%%'.format(100 / (rx4_total + rx6_total + tx4_total + tx6_total) * (rx6_total + tx6_total))); + kpi('ipv6-rx', '%1024.2mB'.format(rx6_total)); + kpi('ipv6-tx', '%1024.2mB'.format(tx6_total)); +} + +function bubbleDismiss() +{ + var bubble = document.getElementById('detail-bubble'); + + bubble.className = ''; + document.body.appendChild(bubble); + + return false; +} + + +//]]></script> + +<h2 name="content"><%:Netlink Bandwidth Monitor%></h2> + +<div id="detail-bubble"> + <span id="bubble-arrow"></span> + <div> + <div class="head"> + <a class="dismiss" href="#" onclick="this.blur(); return bubbleDismiss()">×</a> + <div class="pie"> + <label>Download</label> + <canvas id="bubble-pie1" width="100" height="100"></canvas> + </div> + <div class="pie"> + <label>Upload</label> + <canvas id="bubble-pie2" width="100" height="100"></canvas> + </div> + <div class="kpi"> + <ul> + <li><%_Hostname: <big id="bubble-hostname">example.org</big>%></li> + <li><%_Vendor: <big id="bubble-vendor">Example Corp.</big>%></li> + </ul> + </div> + </div> + <table id="bubble-table"></table> + </div> +</div> + +<hr> + +<p> + <%:Select accounting period:%> + <select id="nlbw.period" style="display:none"></select> +</p> + +<hr> + +<ul class="cbi-tabmenu"> + <li id="tab.nlbw.traffic" class="cbi-tab"><a href="#" onclick="return switchTab('traffic')"><%:Traffic Distribution%></a></li> + <li id="tab.nlbw.layer7" class="cbi-tab-disabled"><a href="#" onclick="return switchTab('layer7')"><%:Application Protocols%></a></li> + <li id="tab.nlbw.ipv6" class="cbi-tab-disabled"><a href="#" onclick="return switchTab('ipv6')"><%:IPv6%></a></li> + <li id="tab.nlbw.export" class="cbi-tab-disabled"><a href="#" onclick="return switchTab('export')"><%:Export%></a></li> +</ul> + +<div class="cbi-section" id="container.nlbw.traffic"> + <div> + <div class="pie"> + <label><%:Traffic / Host%></label> + <canvas id="traf-pie" width="200" height="200"></canvas> + </div> + + <div class="pie"> + <label><%:Connections / Host%></label> + <canvas id="conn-pie" width="200" height="200"></canvas> + </div> + + <div class="kpi"> + <ul> + <li><%_<big id="host-total">0</big> hosts%></li> + <li><%_<big id="rx-total">0</big> download%></li> + <li><%_<big id="tx-total">0</big> upload%></li> + <li><%_<big id="conn-total">0</big> connections%></li> + </ul> + </div> + </div> + <table id="host-data"> + <tr> + <th width="10%" class="hostname"><%:Host%></th> + <th width="5%"><%:MAC%></th> + <th width="5%"><%:Connections%></th> + <th width="30%" colspan="2"><%:Download (Bytes / Packets)%></th> + <th width="30%" colspan="2"><%:Upload (Bytes / Packets)%></th> + </tr> + </table> +</div> + +<div class="cbi-section" id="container.nlbw.layer7" style="display:none"> + <div> + <div class="pie"> + <label><%:Download / Application%></label> + <canvas id="layer7-rx-pie" width="200" height="200"></canvas> + </div> + + <div class="pie"> + <label><%:Upload / Application%></label> + <canvas id="layer7-tx-pie" width="200" height="200"></canvas> + </div> + + <div class="kpi"> + <ul> + <li><%_<big id="layer7-total">0</big> different application protocols%></li> + <li><%_<big id="layer7-most-rx">0</big> cause the most download%></li> + <li><%_<big id="layer7-most-tx">0</big> cause the most upload%></li> + <li><%_<big id="layer7-most-conn">0</big> cause the most connections%></li> + </ul> + </div> + </div> + <table id="layer7-data"> + <tr> + <th width="20%"><%:Application%></th> + <th width="10%"><%:Connections%></th> + <th width="30%" colspan="2"><%:Download (Bytes / Packets)%></th> + <th width="30%" colspan="2"><%:Upload (Bytes / Packets)%></th> + </tr> + </table> +</div> + +<div class="cbi-section" id="container.nlbw.ipv6" style="display:none"> + <div> + <div class="pie"> + <label><%:IPv4 vs. IPv6%></label> + <canvas id="ipv6-share-pie" width="200" height="200"></canvas> + </div> + + <div class="pie"> + <label><%:Dualstack enabled hosts%></label> + <canvas id="ipv6-hosts-pie" width="200" height="200"></canvas> + </div> + + <div class="kpi"> + <ul> + <li><%_<big id="ipv6-hosts">0%</big> IPv6 support rate among hosts%></li> + <li><%_<big id="ipv6-share">0%</big> of the total traffic is IPv6%></li> + <li><%_<big id="ipv6-rx">0B</big> total IPv6 download%></li> + <li><%_<big id="ipv6-tx">0B</big> total IPv6 upload%></li> + </ul> + </div> + </div> + <table id="ipv6-data"> + <tr> + <th width="10%" class="hostname"><%:Host%></th> + <th width="5%"><%:MAC%></th> + <th width="5%"><%:Family%></th> + <th width="40%" colspan="2"><%:Download (Bytes / Packets)%></th> + <th width="40%" colspan="2"><%:Upload (Bytes / Packets)%></th> + </tr> + </table> +</div> + +<div class="cbi-section" id="container.nlbw.export" style="display:none"> + <ul> + <li><a href="<%=url('admin/nlbw/data')%>?type=csv&group_by=mac&order_by=-rx,-tx"><%:CSV, grouped by MAC%></a></li> + <li><a href="<%=url('admin/nlbw/data')%>?type=csv&group_by=ip&order_by=-rx,-tx"><%:CSV, grouped by IP%></a></li> + <li><a href="<%=url('admin/nlbw/data')%>?type=csv&group_by=layer7&order_by=-rx,-tx"><%:CSV, grouped by protocol%></a></li> + <li><a href="<%=url('admin/nlbw/data')%>?type=json"><%:JSON dump%></a></li> + </ul> +</div> + +<script type="text/javascript">//<![CDATA[ + cbi_t_add('nlbw', 'traffic'); + cbi_t_add('nlbw', 'layer7'); + cbi_t_add('nlbw', 'ipv6'); + cbi_t_add('nlbw', 'export'); + + XHR.get('<%=url("admin/nlbw/list")%>', null, function(xhr, res) { + + if (res !== null && typeof(res) === 'object' && res.length > 0) { + trafficPeriods = res; + renderPeriods(); + } + + xhr.open('GET', 'https://raw.githubusercontent.com/jow-/oui-database/master/oui.json', true); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + try { res = JSON.parse(xhr.responseText); } + catch(e) { res = null; } + + if (res !== null && typeof(res) === 'object' && (res.length % 3) === 0) + ouiData = res; + + fetchData(''); + } + }; + xhr.send(null); + }); +//]]></script> + +<%+footer%> diff --git a/applications/luci-app-nlbwmon/po/ja/nlbwmon.po b/applications/luci-app-nlbwmon/po/ja/nlbwmon.po new file mode 100644 index 0000000000..b5931e0dfe --- /dev/null +++ b/applications/luci-app-nlbwmon/po/ja/nlbwmon.po @@ -0,0 +1,387 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ja\n" +"X-Generator: Poedit 2.0.3\n" + +msgid "%d IPv4-only hosts" +msgstr "%d IPv4 限定ホスト" + +msgid "%d IPv6-only hosts" +msgstr "%d IPv6 限定ホスト" + +msgid "%d dual-stack hosts" +msgstr "%d デュアルスタック ホスト" + +msgid "%s and %s" +msgstr "%s, %s" + +msgid "%s, %s and %s" +msgstr "%s, %s, %s" + +msgid "-1 - Restart every last day of month" +msgstr "-1 - 月の最終日" + +msgid "-7 - Restart a week before end of month" +msgstr "-7 - 月の最終日の一週間前" + +msgid "1 - Restart every 1st of month" +msgstr "1 - 毎月1日" + +msgid "10m - frequent commits at the expense of flash wear" +msgstr "10m - フラッシュ媒体への負荷が高い頻繁なコミット(10分)" + +msgid "12h - compromise between risk of data loss and flash wear" +msgstr "12h - データ消失リスクとフラッシュ媒体への負荷の妥協点(12時間)" + +msgid "24h - least flash wear at the expense of data loss risk" +msgstr "24h - データ消失リスクは高いがフラッシュ媒体への負荷は最小(24時間)" + +msgid "30s - refresh twice per minute for reasonably current stats" +msgstr "30s - 現在の状態の把握に適切な1分間に2回のリフレッシュ(30秒)" + +msgid "5m - rarely refresh to avoid frequently clearing conntrack counters" +msgstr "" + +msgid "60s - commit minutely, useful for non-flash storage" +msgstr "60秒 - 1分毎のコミット、非フラッシュ ストレージに有用" + +msgid "<big id=\"conn-total\">0</big> connections" +msgstr "<big id=\"conn-total\">0</big> 接続数" + +msgid "<big id=\"host-total\">0</big> hosts" +msgstr "<big id=\"host-total\">0</big> ホスト数" + +msgid "<big id=\"ipv6-hosts\">0%</big> IPv6 support rate among hosts" +msgstr "<big id=\"ipv6-hosts\">0%</big> 全ホスト中の IPv6 サポート比率" + +msgid "<big id=\"ipv6-rx\">0B</big> total IPv6 download" +msgstr "<big id=\"ipv6-rx\">0B</big> IPv6 総ダウンロード" + +msgid "<big id=\"ipv6-share\">0%</big> of the total traffic is IPv6" +msgstr "<big id=\"ipv6-share\">0%</big> 全トラフィック中の IPv6 の割合" + +msgid "<big id=\"ipv6-tx\">0B</big> total IPv6 upload" +msgstr "<big id=\"ipv6-tx\">0B</big> IPv6 総アップロード" + +msgid "<big id=\"layer7-most-conn\">0</big> cause the most connections" +msgstr "<big id=\"layer7-most-conn\">0</big> 接続数上位" + +msgid "<big id=\"layer7-most-rx\">0</big> cause the most download" +msgstr "<big id=\"layer7-most-rx\">0</big> ダウンロード上位" + +msgid "<big id=\"layer7-most-tx\">0</big> cause the most upload" +msgstr "<big id=\"layer7-most-tx\">0</big> アップロード上位" + +msgid "<big id=\"layer7-total\">0</big> different application protocols" +msgstr "<big id=\"layer7-total\">0</big> アプリケーション プロトコル数" + +msgid "<big id=\"rx-total\">0</big> download" +msgstr "<big id=\"rx-total\">0</big> ダウンロード" + +msgid "<big id=\"tx-total\">0</big> upload" +msgstr "<big id=\"tx-total\">0</big> アップロード" + +msgid "Accounting period" +msgstr "収集期間" + +msgid "Advanced Settings" +msgstr "拡張設定" + +msgid "Application" +msgstr "アプリケーション" + +msgid "Application Protocols" +msgstr "アプリケーション プロトコル" + +msgid "Backup" +msgstr "バックアップ" + +msgid "Bandwidth Monitor" +msgstr "帯域幅モニター" + +msgid "CSV, grouped by IP" +msgstr "CSV(IP によるグループ化)" + +msgid "CSV, grouped by MAC" +msgstr "CSV(MAC によるグループ化)" + +msgid "CSV, grouped by protocol" +msgstr "CSV(プロトコルによるグループ化)" + +msgid "" +"Changing the accounting interval type will invalidate existing databases!" +"<br /><strong><a href=\"%s\">Download backup</a></strong>." +msgstr "" +"既存のデータベースと互換性の無い収集期間の形式が選択されました。<br /" +"><strong><a href=\"%s\">バックアップのダウンロード</a></strong>" + +msgid "" +"Choose \"Day of month\" to restart the accounting period monthly on a " +"specific date, e.g. every 3rd. Choose \"Fixed interval\" to restart the " +"accounting period exactly every N days, beginning at a given date." +msgstr "" +"月毎で設定した日付からのデータの計測を行うには、 \"月間\" を選択します(例: " +"毎月3日)。設定した日数毎にデータの収集を行うには、\"特定の間隔\" を選択しま" +"す。後者の場合、指定された日付から開始されます。" + +msgid "Commit interval" +msgstr "コミット間隔" + +msgid "Compress database" +msgstr "データベースの圧縮" + +msgid "Configuration" +msgstr "設定" + +msgid "Conn." +msgstr "接続数" + +msgid "Connections" +msgstr "接続数" + +msgid "Connections / Host" +msgstr "ホスト毎の接続数" + +msgid "Database directory" +msgstr "データベース ディレクトリ" + +msgid "" +"Database storage directory. One file per accounting period will be placed " +"into this directory." +msgstr "" +"データベースの保存先ディレクトリです。計測期間あたり 1 つのファイルがこのディ" +"レクトリに配置されます。" + +msgid "Day of month" +msgstr "月間" + +msgid "" +"Day of month to restart the accounting period. Use negative values to count " +"towards the end of month, e.g. \"-5\" to specify the 27th of July or the " +"24th of Februrary." +msgstr "" +"月の中で新たな収集期間を開始する日です。月の最終日からの日数をマイナス値で指" +"定することができます(例: 7月27日または2月24日は \"-5\")。" + +msgid "Display" +msgstr "表示" + +msgid "Down. (Bytes / Pkts.)" +msgstr "ダウンロード(Bytes / Pkts.)" + +msgid "Download (Bytes / Packets)" +msgstr "ダウンロード(Bytes / Packets)" + +msgid "Download / Application" +msgstr "ダウンロード / アプリケーション" + +msgid "Download Database Backup" +msgstr "データベース バックアップのダウンロード" + +msgid "Dualstack enabled hosts" +msgstr "デュアルスタック ホスト" + +msgid "Due date" +msgstr "期日" + +msgid "Export" +msgstr "エクスポート" + +msgid "Family" +msgstr "IP 種別" + +msgid "Fixed interval" +msgstr "特定の間隔" + +msgid "Force reload…" +msgstr "強制リロード..." + +msgid "General Settings" +msgstr "全般設定" + +msgid "Generate Backup" +msgstr "バックアップの作成" + +msgid "Host" +msgstr "ホスト" + +msgid "Hostname: <big id=\"bubble-hostname\">example.org</big>" +msgstr "ホスト名: <big id=\"bubble-hostname\">example.org</big>" + +msgid "IPv4 vs. IPv6" +msgstr "IPv4 及び IPv6" + +msgid "IPv6" +msgstr "IPv6" + +msgid "Interval" +msgstr "間隔" + +msgid "" +"Interval at which the temporary in-memory database is committed to the " +"persistent database directory." +msgstr "" +"メモリー上の一時的なデータベースから、永続的なデータベース ディレクトリへのコ" +"ミットを実行する間隔です。" + +msgid "" +"Interval at which traffic counters of still established connections are " +"refreshed from netlink information." +msgstr "" + +msgid "Invalid or empty backup archive" +msgstr "無効または空のバックアップ アーカイブです。" + +msgid "JSON dump" +msgstr "JSON ダンプ" + +msgid "Length of accounting interval in days." +msgstr "収集期間の日数です。" + +msgid "Local interfaces" +msgstr "ローカル インターフェース" + +msgid "Local subnets" +msgstr "ローカル サブネット" + +msgid "MAC" +msgstr "MAC" + +msgid "Maximum entries" +msgstr "最大件数" + +msgid "" +"Maximum number of accounting periods to keep, use zero to keep databases " +"forever." +msgstr "" +"計測データを保持する、収集期間の最大個数です。 '0' を設定した場合、全データを" +"保持します。" + +msgid "Netlink Bandwidth Monitor" +msgstr "Netlink Bandwidth Monitor" + +msgid "Netlink Bandwidth Monitor - Backup / Restore" +msgstr "Netlink Bandwidth Monitor - バックアップ / 復元" + +msgid "Netlink Bandwidth Monitor - Configuration" +msgstr "Netlink Bandwidth Monitor - 設定" + +msgid "No data recorded yet." +msgstr "まだデータがありません。" + +msgid "Only conntrack streams from or to any of these networks are counted." +msgstr "" +"選択されたネットワークにおける conntrack ストリームのみが計測されます。" + +msgid "Only conntrack streams from or to any of these subnets are counted." +msgstr "設定されたサブネットにおける conntrack ストリームのみが計測されます。" + +msgid "Preallocate database" +msgstr "データベースの事前割当" + +msgid "Protocol" +msgstr "プロトコル" + +msgid "Protocol Mapping" +msgstr "プロトコル マッピング" + +msgid "" +"Protocol mappings to distinguish traffic types per host, one mapping per " +"line. The first value specifies the IP protocol, the second value the port " +"number and the third column is the name of the mapped protocol." +msgstr "" +"ホスト毎のトラフィック形式を区別するためのプロトコル マッピングで、一行あたり" +"一つのマッピングを追加します。各エントリーの一つ目の値は IP プロトコルを、2つ" +"目の値はポート番号、3つ目はマッピングされたプロトコルの名前をそれぞれ表しま" +"す。" + +msgid "Refresh interval" +msgstr "リフレッシュ間隔" + +msgid "Restore" +msgstr "復元" + +msgid "Restore Database Backup" +msgstr "データベースの復元" + +msgid "Select accounting period:" +msgstr "収集期間を選択:" + +msgid "Source IP" +msgstr "アクセス元 IP" + +msgid "Start date" +msgstr "開始日" + +msgid "Start date of the first accounting period, e.g. begin of ISP contract." +msgstr "初回のデータ収集の開始日です(例: ISP 契約の開始日)。" + +msgid "Stored periods" +msgstr "保存期間" + +msgid "" +"The Netlink Bandwidth Monitor (nlbwmon) is a lightweight, efficient traffic " +"accounting program keeping track of bandwidth usage per host and protocol." +msgstr "" +"Netlink Bandwidth Monitor (nlbwmon) は、軽量かつ、ホストやプロトコル毎に帯域" +"幅使用量の追跡を行う効率的なトラフィック計測プログラムです。" + +msgid "The following database files have been restored: %s" +msgstr "次のデータベース ファイルが復元されました: %s" + +msgid "" +"The maximum amount of entries that should be put into the database, setting " +"the limit to 0 will allow databases to grow indefinitely." +msgstr "" +"データベースに保管される最大件数です。 '0' を設定した場合、制限無しのデータ" +"ベースの増大を許可します。" + +msgid "Traffic / Host" +msgstr "トラフィック / ホスト" + +msgid "Traffic Distribution" +msgstr "トラフィック内訳" + +msgid "Up. (Bytes / Pkts.)" +msgstr "アップロード(Bytes / Pkts.)" + +msgid "Upload (Bytes / Packets)" +msgstr "アップロード(Bytes / Packets)" + +msgid "Upload / Application" +msgstr "アップロード / アプリケーション" + +msgid "Vendor: <big id=\"bubble-vendor\">Example Corp.</big>" +msgstr "ベンダ: <big id=\"bubble-vendor\">Example Corp.</big>" + +msgid "Warning" +msgstr "警告" + +msgid "" +"Whether to gzip compress archive databases. Compressing the database files " +"makes accessing old data slightly slower but helps to reduce storage " +"requirements." +msgstr "" +"データベースの gzip 圧縮アーカイブ化です。データベース ファイルを圧縮すると古" +"いデータへのアクセスが多少遅くなりますが、ストレージ使用量の低減に役立ちま" +"す。" + +msgid "" +"Whether to preallocate the maximum possible database size in memory. This is " +"mainly useful for memory constrained systems which might not be able to " +"satisfy memory allocation after longer uptime periods." +msgstr "" + +msgid "no traffic" +msgstr "トラフィック無し" + +msgid "other" +msgstr "その他" diff --git a/applications/luci-app-nlbwmon/po/templates/nlbwmon.pot b/applications/luci-app-nlbwmon/po/templates/nlbwmon.pot new file mode 100644 index 0000000000..61d2230793 --- /dev/null +++ b/applications/luci-app-nlbwmon/po/templates/nlbwmon.pot @@ -0,0 +1,352 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "%d IPv4-only hosts" +msgstr "" + +msgid "%d IPv6-only hosts" +msgstr "" + +msgid "%d dual-stack hosts" +msgstr "" + +msgid "%s and %s" +msgstr "" + +msgid "%s, %s and %s" +msgstr "" + +msgid "-1 - Restart every last day of month" +msgstr "" + +msgid "-7 - Restart a week before end of month" +msgstr "" + +msgid "1 - Restart every 1st of month" +msgstr "" + +msgid "10m - frequent commits at the expense of flash wear" +msgstr "" + +msgid "12h - compromise between risk of data loss and flash wear" +msgstr "" + +msgid "24h - least flash wear at the expense of data loss risk" +msgstr "" + +msgid "30s - refresh twice per minute for reasonably current stats" +msgstr "" + +msgid "5m - rarely refresh to avoid frequently clearing conntrack counters" +msgstr "" + +msgid "60s - commit minutely, useful for non-flash storage" +msgstr "" + +msgid "<big id=\"conn-total\">0</big> connections" +msgstr "" + +msgid "<big id=\"host-total\">0</big> hosts" +msgstr "" + +msgid "<big id=\"ipv6-hosts\">0%</big> IPv6 support rate among hosts" +msgstr "" + +msgid "<big id=\"ipv6-rx\">0B</big> total IPv6 download" +msgstr "" + +msgid "<big id=\"ipv6-share\">0%</big> of the total traffic is IPv6" +msgstr "" + +msgid "<big id=\"ipv6-tx\">0B</big> total IPv6 upload" +msgstr "" + +msgid "<big id=\"layer7-most-conn\">0</big> cause the most connections" +msgstr "" + +msgid "<big id=\"layer7-most-rx\">0</big> cause the most download" +msgstr "" + +msgid "<big id=\"layer7-most-tx\">0</big> cause the most upload" +msgstr "" + +msgid "<big id=\"layer7-total\">0</big> different application protocols" +msgstr "" + +msgid "<big id=\"rx-total\">0</big> download" +msgstr "" + +msgid "<big id=\"tx-total\">0</big> upload" +msgstr "" + +msgid "Accounting period" +msgstr "" + +msgid "Advanced Settings" +msgstr "" + +msgid "Application" +msgstr "" + +msgid "Application Protocols" +msgstr "" + +msgid "Backup" +msgstr "" + +msgid "Bandwidth Monitor" +msgstr "" + +msgid "CSV, grouped by IP" +msgstr "" + +msgid "CSV, grouped by MAC" +msgstr "" + +msgid "CSV, grouped by protocol" +msgstr "" + +msgid "" +"Changing the accounting interval type will invalidate existing databases!" +"<br /><strong><a href=\"%s\">Download backup</a></strong>." +msgstr "" + +msgid "" +"Choose \"Day of month\" to restart the accounting period monthly on a " +"specific date, e.g. every 3rd. Choose \"Fixed interval\" to restart the " +"accounting period exactly every N days, beginning at a given date." +msgstr "" + +msgid "Commit interval" +msgstr "" + +msgid "Compress database" +msgstr "" + +msgid "Configuration" +msgstr "" + +msgid "Conn." +msgstr "" + +msgid "Connections" +msgstr "" + +msgid "Connections / Host" +msgstr "" + +msgid "Database directory" +msgstr "" + +msgid "" +"Database storage directory. One file per accounting period will be placed " +"into this directory." +msgstr "" + +msgid "Day of month" +msgstr "" + +msgid "" +"Day of month to restart the accounting period. Use negative values to count " +"towards the end of month, e.g. \"-5\" to specify the 27th of July or the " +"24th of Februrary." +msgstr "" + +msgid "Display" +msgstr "" + +msgid "Down. (Bytes / Pkts.)" +msgstr "" + +msgid "Download (Bytes / Packets)" +msgstr "" + +msgid "Download / Application" +msgstr "" + +msgid "Download Database Backup" +msgstr "" + +msgid "Dualstack enabled hosts" +msgstr "" + +msgid "Due date" +msgstr "" + +msgid "Export" +msgstr "" + +msgid "Family" +msgstr "" + +msgid "Fixed interval" +msgstr "" + +msgid "Force reload…" +msgstr "" + +msgid "General Settings" +msgstr "" + +msgid "Generate Backup" +msgstr "" + +msgid "Host" +msgstr "" + +msgid "Hostname: <big id=\"bubble-hostname\">example.org</big>" +msgstr "" + +msgid "IPv4 vs. IPv6" +msgstr "" + +msgid "IPv6" +msgstr "" + +msgid "Interval" +msgstr "" + +msgid "" +"Interval at which the temporary in-memory database is committed to the " +"persistent database directory." +msgstr "" + +msgid "" +"Interval at which traffic counters of still established connections are " +"refreshed from netlink information." +msgstr "" + +msgid "Invalid or empty backup archive" +msgstr "" + +msgid "JSON dump" +msgstr "" + +msgid "Length of accounting interval in days." +msgstr "" + +msgid "Local interfaces" +msgstr "" + +msgid "Local subnets" +msgstr "" + +msgid "MAC" +msgstr "" + +msgid "Maximum entries" +msgstr "" + +msgid "" +"Maximum number of accounting periods to keep, use zero to keep databases " +"forever." +msgstr "" + +msgid "Netlink Bandwidth Monitor" +msgstr "" + +msgid "Netlink Bandwidth Monitor - Backup / Restore" +msgstr "" + +msgid "Netlink Bandwidth Monitor - Configuration" +msgstr "" + +msgid "No data recorded yet." +msgstr "" + +msgid "Only conntrack streams from or to any of these networks are counted." +msgstr "" + +msgid "Only conntrack streams from or to any of these subnets are counted." +msgstr "" + +msgid "Preallocate database" +msgstr "" + +msgid "Protocol" +msgstr "" + +msgid "Protocol Mapping" +msgstr "" + +msgid "" +"Protocol mappings to distinguish traffic types per host, one mapping per " +"line. The first value specifies the IP protocol, the second value the port " +"number and the third column is the name of the mapped protocol." +msgstr "" + +msgid "Refresh interval" +msgstr "" + +msgid "Restore" +msgstr "" + +msgid "Restore Database Backup" +msgstr "" + +msgid "Select accounting period:" +msgstr "" + +msgid "Source IP" +msgstr "" + +msgid "Start date" +msgstr "" + +msgid "Start date of the first accounting period, e.g. begin of ISP contract." +msgstr "" + +msgid "Stored periods" +msgstr "" + +msgid "" +"The Netlink Bandwidth Monitor (nlbwmon) is a lightweight, efficient traffic " +"accounting program keeping track of bandwidth usage per host and protocol." +msgstr "" + +msgid "The following database files have been restored: %s" +msgstr "" + +msgid "" +"The maximum amount of entries that should be put into the database, setting " +"the limit to 0 will allow databases to grow indefinitely." +msgstr "" + +msgid "Traffic / Host" +msgstr "" + +msgid "Traffic Distribution" +msgstr "" + +msgid "Up. (Bytes / Pkts.)" +msgstr "" + +msgid "Upload (Bytes / Packets)" +msgstr "" + +msgid "Upload / Application" +msgstr "" + +msgid "Vendor: <big id=\"bubble-vendor\">Example Corp.</big>" +msgstr "" + +msgid "Warning" +msgstr "" + +msgid "" +"Whether to gzip compress archive databases. Compressing the database files " +"makes accessing old data slightly slower but helps to reduce storage " +"requirements." +msgstr "" + +msgid "" +"Whether to preallocate the maximum possible database size in memory. This is " +"mainly useful for memory constrained systems which might not be able to " +"satisfy memory allocation after longer uptime periods." +msgstr "" + +msgid "no traffic" +msgstr "" + +msgid "other" +msgstr "" diff --git a/applications/luci-app-nlbwmon/root/etc/uci-defaults/40_luci-nlbwmon b/applications/luci-app-nlbwmon/root/etc/uci-defaults/40_luci-nlbwmon new file mode 100644 index 0000000000..c9771779ee --- /dev/null +++ b/applications/luci-app-nlbwmon/root/etc/uci-defaults/40_luci-nlbwmon @@ -0,0 +1,11 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@nlbwmon[-1] + add ucitrack nlbwmon + set ucitrack.@nlbwmon[-1].init=nlbwmon + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/applications/luci-app-shadowsocks-libev/Makefile b/applications/luci-app-shadowsocks-libev/Makefile index 848a5c8317..d0923e07a4 100644 --- a/applications/luci-app-shadowsocks-libev/Makefile +++ b/applications/luci-app-shadowsocks-libev/Makefile @@ -1,14 +1,16 @@ # -# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org> +# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com> # # This is free software, licensed under the Apache License, Version 2.0 . # include $(TOPDIR)/rules.mk -LUCI_TITLE:=LuCI Support for Shadowsocks-libev +LUCI_TITLE:=LuCI Support for shadowsocks-libev LUCI_DEPENDS:= +PKG_LICENSE:=Apache-2.0 + include ../../luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua index ae968168f1..05d12e38b2 100644 --- a/applications/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua +++ b/applications/luci-app-shadowsocks-libev/luasrc/controller/shadowsocks-libev.lua @@ -1,12 +1,33 @@ --- Copyright 2015 Jian Chang <aa65535@live.com> +-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com> -- Licensed to the public under the Apache License 2.0. - +-- module("luci.controller.shadowsocks-libev", package.seeall) function index() - if not nixio.fs.access("/etc/config/shadowsocks-libev") then - return - end + entry({"admin", "services", "shadowsocks-libev"}, + alias("admin", "services", "shadowsocks-libev", "instances"), + _("Shadowsocks-libev"), 59) + + entry({"admin", "services", "shadowsocks-libev", "instances"}, + arcombine(cbi("shadowsocks-libev/instances"), cbi("shadowsocks-libev/instance-details")), + _("Local Instances"), 10).leaf = true + + entry({"admin", "services", "shadowsocks-libev", "servers"}, + cbi("shadowsocks-libev/servers"), + _("Remote Servers"), 20).leaf = true + + entry({"admin", "services", "shadowsocks-libev", "rules"}, + cbi("shadowsocks-libev/rules"), + _("Redir Rules"), 30).leaf = true + + entry({"admin", "services", "shadowsocks-libev", "status"}, call("ss_status"), nil).leaf = true + +end + +function ss_status() + local ut = require "luci.util" + local rv = ut.ubus("service", "list", {name = "shadowsocks-libev"})["shadowsocks-libev"] or {_=0} - entry({"admin", "services", "shadowsocks-libev"}, cbi("shadowsocks-libev"), _("ShadowSocks-libev"), 74).dependent = true + luci.http.prepare_content("application/json") + luci.http.write_json(rv) end diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua deleted file mode 100644 index 97ce83f771..0000000000 --- a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev.lua +++ /dev/null @@ -1,157 +0,0 @@ --- Copyright 2015 Jian Chang <aa65535@live.com> --- Licensed to the public under the Apache License 2.0. - -local m, s, o, e, a - -if luci.sys.call("pidof ss-redir >/dev/null") == 0 then - m = Map("shadowsocks-libev", translate("ShadowSocks-libev"), translate("ShadowSocks-libev is running")) -else - m = Map("shadowsocks-libev", translate("ShadowSocks-libev"), translate("ShadowSocks-libev is not running")) -end - -e = { - "table", - "rc4", - "rc4-md5", - "aes-128-cfb", - "aes-192-cfb", - "aes-256-cfb", - "bf-cfb", - "camellia-128-cfb", - "camellia-192-cfb", - "camellia-256-cfb", - "cast5-cfb", - "des-cfb", - "idea-cfb", - "rc2-cfb", - "seed-cfb", - "salsa20", - "chacha20", -} - --- Global Setting -s = m:section(TypedSection, "shadowsocks-libev", translate("Global Setting")) -s.anonymous = true - -o = s:option(Flag, "enable", translate("Enable")) -o.default = 1 -o.rmempty = false - -o = s:option(Value, "server", translate("Server Address")) -o.datatype = "ipaddr" -o.rmempty = false - -o = s:option(Value, "server_port", translate("Server Port")) -o.datatype = "port" -o.rmempty = false - -o = s:option(Value, "local_port", translate("Local Port")) -o.datatype = "port" -o.default = 1080 -o.rmempty = false - -o = s:option(Value, "timeout", translate("Connection Timeout")) -o.datatype = "uinteger" -o.default = 60 -o.rmempty = false - -o = s:option(Value, "password", translate("Password")) -o.password = true -o.rmempty = false - -o = s:option(ListValue, "encrypt_method", translate("Encrypt Method")) -for i,v in ipairs(e) do - o:value(v) -end -o.rmempty = false - -o = s:option(Value, "ignore_list", translate("Ignore List")) -o:value("/dev/null", translate("Disabled")) -o.default = "/dev/null" -o.rmempty = false - --- UDP Relay -s = m:section(TypedSection, "shadowsocks-libev", translate("UDP Relay")) -s.anonymous = true - -o = s:option(ListValue, "udp_mode", translate("Relay Mode")) -o:value("0", translate("Disabled")) -o:value("1", translate("Enabled")) -o:value("2", translate("Custom")) -o.default = 0 -o.rmempty = false - -o = s:option(Value, "udp_server", translate("Server Address")) -o.datatype = "ipaddr" -o:depends("udp_mode", 2) - -o = s:option(Value, "udp_server_port", translate("Server Port")) -o.datatype = "port" -o:depends("udp_mode", 2) - -o = s:option(Value, "udp_local_port", translate("Local Port")) -o.datatype = "port" -o.default = 1081 -o:depends("udp_mode", 2) - -o = s:option(Value, "udp_timeout", translate("Connection Timeout")) -o.datatype = "uinteger" -o.default = 60 -o:depends("udp_mode", 2) - -o = s:option(Value, "udp_password", translate("Password")) -o.password = true -o:depends("udp_mode", 2) - -o = s:option(ListValue, "udp_encrypt_method", translate("Encrypt Method")) -for i,v in ipairs(e) do - o:value(v) -end -o:depends("udp_mode", 2) - --- UDP Forward -s = m:section(TypedSection, "shadowsocks-libev", translate("UDP Forward")) -s.anonymous = true - -o = s:option(Flag, "tunnel_enable", translate("Enable")) -o.default = 1 -o.rmempty = false - -o = s:option(Value, "tunnel_port", translate("UDP Local Port")) -o.datatype = "port" -o.default = 5300 - -o = s:option(Value, "tunnel_forward", translate("Forwarding Tunnel")) -o.default = "8.8.4.4:53" - --- Access Control -s = m:section(TypedSection, "shadowsocks-libev", translate("Access Control")) -s.anonymous = true - -s:tab("lan_ac", translate("LAN")) - -o = s:taboption("lan_ac", ListValue, "lan_ac_mode", translate("Access Control")) -o:value("0", translate("Disabled")) -o:value("1", translate("Allow listed only")) -o:value("2", translate("Allow all except listed")) -o.default = 0 -o.rmempty = false - -o = s:taboption("lan_ac", DynamicList, "lan_ac_ip", translate("LAN IP List")) -o.datatype = "ipaddr" - -luci.ip.neighbors({ family = 4 }, function(entry) - if entry.reachable then - o:value(entry.dest:string()) - end -end) - -s:tab("wan_ac", translate("WAN")) - -o = s:taboption("wan_ac", DynamicList, "wan_bp_ip", translate("Bypassed IP")) -o.datatype = "ip4addr" - -o = s:taboption("wan_ac", DynamicList, "wan_fw_ip", translate("Forwarded IP")) -o.datatype = "ip4addr" - -return m diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua new file mode 100644 index 0000000000..d9a61d0bf7 --- /dev/null +++ b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instance-details.lua @@ -0,0 +1,49 @@ +-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com> +-- Licensed to the public under the Apache License 2.0. + +local ds = require "luci.dispatcher" +local ss = require "luci.model.shadowsocks-libev" + +local sname = arg[1] +local redirect_url = ds.build_url("admin/services/shadowsocks-libev/instances") +local s, o + +local m = Map("shadowsocks-libev") +local sdata = m:get(sname) +if not sdata then + luci.http.redirect(redirect_url) + return +end +local stype = sdata[".type"] +m.redirect = redirect_url +m.title = "shadowsocks-libev - %s - %s" % {stype, sname} + + +s = m:section(NamedSection, sname, stype) +s:tab("general", translate("General Settings")) +s:tab("advanced", translate("Advanced Settings")) +s:taboption("general", Flag, "disabled", translate("Disable")) +ss.option_install_package(s, "general") + +if stype == "ss_server" then + ss.options_server(s, "general") + o = s:taboption("general", Value, "bind_address", + translate("Bind address"), + translate("The address ss-server will initiate connection from")) + o.datatype = "ipaddr" + o.placeholder = "0.0.0.0" + ss.values_ipaddr(o) + o = s:taboption("general", Value, "manager_address", translate("Manager address")) + o.datatype = "hostport" +else + ss.options_client(s, "general") + if stype == "ss_tunnel" then + o = s:taboption("general", Value, "tunnel_address", + translate("Tunnel address"), + translate("The address ss-tunnel will forward traffic to")) + o.datatype = "hostport" + end +end +ss.options_common(s, "advanced") + +return m diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua new file mode 100644 index 0000000000..62a90fb416 --- /dev/null +++ b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/instances.lua @@ -0,0 +1,104 @@ +-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com> +-- Licensed to the public under the Apache License 2.0. + +local ds = require "luci.dispatcher" +local ss = require "luci.model.shadowsocks-libev" +local ut = require "luci.util" +local m, s, o + +m = Map("shadowsocks-libev", + translate("Local Instances"), + translate("Instances of shadowsocks-libev components, e.g. ss-local, \ + ss-redir, ss-tunnel, ss-server, etc. To enable an instance it \ + is required to enable both the instance itself and the remote \ + server it refers to.")) + +local instances = {} +local cfgtypes = { "ss_local", "ss_redir", "ss_server", "ss_tunnel" } + +for sname, sdata in pairs(m:get()) do + local key, value = ss.cfgvalue_overview(sdata) + if key ~= nil then + instances[key] = value + end +end + +s = m:section(Table, instances) +s.addremove = true +s.template_addremove = "shadowsocks-libev/add_instance" +s.extedit = function(self, section) + local value = instances[section] + if type(value) == "table" then + return ds.build_url(unpack(ds.context.requestpath), + "services/shadowsocks-libev/instances", + value[".name"]) + end +end +s.parse = function(self, ...) + Table.parse(self, ...) + + local crval = REMOVE_PREFIX .. self.config + local name = self.map:formvaluetable(crval) + for k,v in pairs(name) do + local value = instances[k] + local sname = value[".name"] + if type(value) == "table" then + m:del(sname) + instances[k] = nil + for _, oname in ipairs({"redir_tcp", "redir_udp"}) do + local ovalue = m:get("ss_rules", oname) + if ovalue == sname then + m:del("ss_rules", oname) + end + end + end + end + + local stype = m:formvalue("_newinst.type") + local sname = m:formvalue("_newinst.name") + if ut.contains(cfgtypes, stype) then + local created + if sname and #sname > 0 then + created = m:set(sname, nil, stype) + else + created = m:add(stype) + sname = created + end + if created then + m.uci:save("shadowsocks-libev") + luci.http.redirect(ds.build_url( + "admin/services/shadowsocks-libev/instances", sname + )) + end + end +end + +o = s:option(DummyValue, "name", translate("Name")) +o.rawhtml = true +o = s:option(DummyValue, "overview", translate("Overview")) +o.rawhtml = true + +s:option(DummyValue, "running", translate("Running")) + +o = s:option(Button, "disabled", translate("Enable/Disable")) +o.render = function(self, section, scope) + if instances[section].disabled then + self.title = translate("Disabled") + self.inputstyle = "reset" + else + self.title = translate("Enabled") + self.inputstyle = "save" + end + Button.render(self, section, scope) +end +o.write = function(self, section) + local sdata = instances[section] + if type(sdata) == "table" then + local sname = sdata[".name"] + local disabled = not sdata["disabled"] + sdata["disabled"] = disabled + m:set(sname, "disabled", tostring(disabled)) + end +end + +return m diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua new file mode 100644 index 0000000000..fe5f9c31b8 --- /dev/null +++ b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua @@ -0,0 +1,73 @@ +-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com> +-- Licensed to the public under the Apache License 2.0. + +local ss = require("luci.model.shadowsocks-libev") + +local m, s, o + +m = Map("shadowsocks-libev", + translate("Redir Rules"), + translate("On this page you can configure how traffics are to be \ + forwarded to ss-redir instances. \ + If enabled, packets will first have their source ip addresses checked \ + against <em>Src ip bypass</em>, <em>Src ip forward</em>, \ + <em>Src ip checkdst</em> and if none matches <em>Src default</em> \ + will give the default action to be taken. \ + If the prior check results in action <em>checkdst</em>, packets will continue \ + to have their destination addresses checked.")) + + +s = m:section(NamedSection, "ss_rules", "ss-rules") +s:tab("general", translate("General Settings")) +s:tab("srcip", translate("Source Settings")) +s:tab("dstip", translate("Destination Settings")) + +s:taboption('general', Flag, "disabled", translate("Disable")) +ss.option_install_package(s, 'general') + +o = s:taboption('general', ListValue, "redir_tcp", + translate("ss-redir for TCP")) +ss.values_redir(o, 'tcp') +o = s:taboption('general', ListValue, "redir_udp", + translate("ss-redir for UDP")) +ss.values_redir(o, 'udp') + +o = s:taboption('general', ListValue, "local_default", + translate("Local-out default"), + translate("Default action for locally generated packets")) +ss.values_actions(o) +s:taboption('general', Value, "ipt_args", + translate("Extra arguments"), + translate("Passes additional arguments to iptables. Use with care!")) + +s:taboption('srcip', DynamicList, "src_ips_bypass", + translate("Src ip bypass"), + translate("Bypass redir action for packets with source addresses in this list")) +s:taboption('srcip', DynamicList, "src_ips_forward", + translate("Src ip forward"), + translate("Go through redir action for packets with source addresses in this list")) +s:taboption('srcip', DynamicList, "src_ips_checkdst", + translate("Src ip checkdst"), + translate("Continue to have dst address checked for packets with source addresses in this list")) +o = s:taboption('srcip', ListValue, "src_default", + translate("Src default"), + translate("Default action for packets whose source addresses do not match any of the source ip list")) +ss.values_actions(o) + +s:taboption('dstip', DynamicList, "dst_ips_bypass", + translate("Dst ip bypass"), + translate("Bypass redir action for packets with destination addresses in this list")) +s:taboption('dstip', DynamicList, "dst_ips_forward", + translate("Dst ip forward"), + translate("Go through redir action for packets with destination addresses in this list")) + +o = s:taboption('dstip', FileBrowser, "dst_ips_bypass_file", + translate("Dst ip bypass file"), + translate("File containing ip addresses for the purposes as with <em>Dst ip bypass</em>")) +o.datatype = "file" +s:taboption('dstip', FileBrowser, "dst_ips_forward_file", + translate("Dst ip forward file"), + translate("File containing ip addresses for the purposes as with <em>Dst ip forward</em>")) +o.datatype = "file" + +return m diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua new file mode 100644 index 0000000000..71c66562e1 --- /dev/null +++ b/applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/servers.lua @@ -0,0 +1,31 @@ +-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com> +-- Licensed to the public under the Apache License 2.0. + +local ds = require "luci.dispatcher" +local ss = require("luci.model.shadowsocks-libev") + +local m, s + +m = Map("shadowsocks-libev", + translate("Remote Servers"), + translate("Definition of remote shadowsocks servers. \ + Disable any of them will also disable instances refering to it.")) + +local sname = arg[1] +if sname then + if not m:get(sname) then + luci.http.redirect(ds.build_url("admin/services/shadowsocks-libev/servers")) + return + end + s = m:section(NamedSection, sname, "server") + m.title = m.title .. ' - ' .. sname +else + s = m:section(TypedSection, "server") + s.template = 'cbi/tblsection' + s.addremove = true +end + +s:option(Flag, "disabled", translate("Disable")) +ss.options_server(s) + +return m diff --git a/applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua b/applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua new file mode 100644 index 0000000000..b11890f89d --- /dev/null +++ b/applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua @@ -0,0 +1,252 @@ +-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com> +-- Licensed to the public under the Apache License 2.0. + +local _up = getfenv(3) +local ut = require("luci.util") +local ds = require("luci.dispatcher") +local nw = require("luci.model.network") +nw.init() +module("luci.model.shadowsocks-libev", function(m) + setmetatable(m, {__index=function (self, k) + local tb = _up + return rawget(self, k) or _up[k] + end}) +end) + +function values_actions(o) + for _, a in ipairs(actions) do + o:value(a) + end +end + +function values_redir(o, xmode) + o.map.uci.foreach("shadowsocks-libev", "ss_redir", function(sdata) + local sname = sdata[".name"] + local mode = sdata["mode"] + if mode and mode:find(xmode) then + local desc = "%s - %s" % {sname, mode} + o:value(sname, desc) + end + end) +end + +function values_serverlist(o) + o.map.uci.foreach("shadowsocks-libev", "server", function(sdata) + local sname = sdata[".name"] + local server = sdata["server"] + local server_port = sdata["server_port"] + if server and server_port then + local desc = "%s - %s:%s" % {sname, sdata["server"], sdata["server_port"]} + o:value(sname, desc) + end + end) +end + +function values_ipaddr(o) + local keys, vals = {}, {} + for _, v in ipairs(nw:get_interfaces()) do + for _, a in ipairs(v:ipaddrs()) do + o:value(a:host():string(), '%s (%s)' %{ a:host(), v:shortname() }) + end + end +end + +function options_client(s, tab) + local o + + o = s:taboption(tab, ListValue, "server", translate("Remote server")) + values_serverlist(o) + o = s:taboption(tab, Value, "local_address", translate("Local address")) + o.datatype = "ipaddr" + o.placeholder = "0.0.0.0" + values_ipaddr(o) + o = s:taboption(tab, Value, "local_port", translate("Local port")) + o.datatype = "port" +end + +function options_server(s, tab) + local o + local optfunc + + if tab == nil then + optfunc = function(...) return s:option(...) end + else + optfunc = function(...) return s:taboption(tab, ...) end + end + + o = optfunc(Value, "server", translate("Server")) + o.datatype = "host" + o.size = 16 + o = optfunc(Value, "server_port", translate("Server port")) + o.datatype = "port" + o.size = 5 + o = optfunc(ListValue, "method", translate("Method")) + for _, m in ipairs(methods) do + o:value(m) + end + o = optfunc(Value, "key", translate("Key (base64 encoding)")) + o.datatype = "base64" + o.password = true + o.size = 12 + o = optfunc(Value, "password", translate("Password")) + o.password = true + o.size = 12 +end + +function options_common(s, tab) + local o + + o = s:taboption(tab, ListValue, "mode", translate("Mode of operation")) + for _, m in ipairs(modes) do + o:value(m) + end + o.default = "tcp_and_udp" + o = s:taboption(tab, Value, "mtu", translate("MTU")) + o.datatype = "uinteger" + o = s:taboption(tab, Value, "timeout", translate("Timeout (sec)")) + o.datatype = "uinteger" + s:taboption(tab, Value, "user", translate("Run as")) + + s:taboption(tab, Flag, "verbose", translate("Verbose")) + s:taboption(tab, Flag, "ipv6_first", translate("IPv6 First"), translate("Prefer IPv6 addresses when resolving names")) + s:taboption(tab, Flag, "fast_open", translate("Enable TCP Fast Open")) + s:taboption(tab, Flag, "reuse_port", translate("Enable SO_REUSEPORT")) +end + +function ucival_to_bool(val) + return val == "true" or val == "1" or val == "yes" or val == "on" +end + +function cfgvalue_overview(sdata) + local stype = sdata[".type"] + local lines = {} + + if stype == "ss_server" then + cfgvalue_overview_(sdata, lines, names_options_server) + cfgvalue_overview_(sdata, lines, names_options_common) + cfgvalue_overview_(sdata, lines, { + "bind_address", + "manager_address", + }) + elseif stype == "ss_local" or stype == "ss_redir" or stype == "ss_tunnel" then + cfgvalue_overview_(sdata, lines, names_options_client) + if stype == "ss_tunnel" then + cfgvalue_overview_(sdata, lines, {"tunnel_address"}) + end + cfgvalue_overview_(sdata, lines, names_options_common) + else + return nil, nil + end + local sname = sdata[".name"] + local key = "%s.%s" % {stype, sname} + local value = { + [".name"] = sname, + name = '%s.<var>%s</var>' % {stype, sname}, + overview = table.concat(lines, "</br>"), + disabled = ucival_to_bool(sdata["disabled"]), + } + return key, value +end + +function cfgvalue_overview_(sdata, lines, names) + local line + + for _, n in ipairs(names) do + local v = sdata[n] + if v ~= nil then + local fv = "<var>%s</var>" % ut.pcdata(v) + if sdata[".type"] ~= "ss_server" and n == "server" then + fv = '<a class="label" href="%s">%s</a>' % { + ds.build_url("admin/services/shadowsocks-libev/servers", v), fv} + end + line = n .. ": " .. fv + table.insert(lines, line) + end + end +end + +function option_install_package(s, tab) + local bin = s.sectiontype:gsub("_", "-", 1) + local installed = nixio.fs.access("/usr/bin/" .. bin) + if installed then + return + end + local opkg_package = "shadowsocks-libev-" .. bin + local p_install + if tab then + p_install = s:taboption(tab, Button, "_install") + else + p_install = s:option(Button, "_install") + end + p_install.title = translate("Package is not installed") + p_install.inputtitle = translate("Install package %q" % opkg_package) + p_install.inputstyle = "apply" + + function p_install.write() + return luci.http.redirect( + luci.dispatcher.build_url("admin/system/packages") .. + "?submit=1&install=%s" % opkg_package + ) + end +end + +names_options_server = { + "server", + "server_port", + "method", + "key", + "password", +} + +names_options_client = { + "server", + "local_address", + "local_port", +} + +names_options_common = { + "verbose", + "ipv6_first", + "fast_open", + "reuse_port", + "mode", + "mtu", + "timeout", + "user", +} + +modes = { + "tcp_only", + "tcp_and_udp", + "udp_only", +} + +actions = { + "bypass", + "forward", + "checkdst", +} + +methods = { + -- aead + "aes-128-gcm", + "aes-192-gcm", + "aes-256-gcm", + -- stream + "table", + "rc4", + "rc4-md5", + "aes-128-cfb", + "aes-192-cfb", + "aes-256-cfb", + "aes-128-ctr", + "aes-192-ctr", + "aes-256-ctr", + "bf-cfb", + "camellia-128-cfb", + "camellia-192-cfb", + "camellia-256-cfb", + "salsa20", + "chacha20", + "chacha20-ietf", +} diff --git a/applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm b/applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm new file mode 100644 index 0000000000..219d89b074 --- /dev/null +++ b/applications/luci-app-shadowsocks-libev/luasrc/view/shadowsocks-libev/add_instance.htm @@ -0,0 +1,45 @@ +<div class="cbi-section-create cbi-tblsection-create"> + <br /> + <table class="cbi-section-table"> + <tr class="cbi-section-table-row"> + <td class="cbi-section-table-cell" style="width:140px"> + <select class="cbi-input-select" id="_newinst.type" name="_newinst.type"> + <option value="_dummy">-- instance type --</option> + <option value="ss_local">ss-local</option> + <option value="ss_tunnel">ss-tunnel</option> + <option value="ss_redir">ss-redir</option> + <option value="ss_server">ss-server</option> + </select> + </td> + <td class="cbi-section-table-cell" style="width:110px"> + <input type="text" class="cbi-input-text" id="_newinst.name" name="_newinst.name" placeholder="<%:Name%>"/> + </td> + <td class="cbi-section-table-cell left"> + <input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>" value="<%:Add%>" /> + </td> + </tr> + </table> +</div> +<script type="text/javascript">//<![CDATA[ + XHR.poll(5, '<%=url('admin/services/shadowsocks-libev/status')%>', null, + function(x, st) + { + var names = [ + <%- + for _, name in ipairs(self:cfgsections()) do + write("%q," % name) + end + -%> + ]; + var instances = st["instances"] || {}; + for (var i = 0, len = names.length; i < len; i++) { + var name = names[i]; + var el = document.getElementById('cbi-table-' + name + '-running'); + if (el) { + var running = instances.hasOwnProperty(name)? instances[name].running : false; + el.innerText = running ? 'yes' : 'no'; + } + } + } + ); +//]]></script> diff --git a/applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po deleted file mode 100644 index f2b18e374c..0000000000 --- a/applications/luci-app-shadowsocks-libev/po/pt-br/shadowsocks-libev.po +++ /dev/null @@ -1,97 +0,0 @@ -msgid "" -msgstr "" -"Content-Type: text/plain; charset=UTF-8\n" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.11\n" -"Last-Translator: Luiz Angelo Daros de Luca <luizluca@gmail.com>\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"Language: pt_BR\n" - -msgid "Access Control" -msgstr "Controle de Acesso" - -msgid "Allow all except listed" -msgstr "Permitir todos, exceto os listados" - -msgid "Allow listed only" -msgstr "Permitir somente os listados" - -msgid "Bypassed IP" -msgstr "Endereços IP Ignorados" - -msgid "Connection Timeout" -msgstr "Tempo limite de conexão" - -msgid "Custom" -msgstr "Personalizado" - -msgid "Disabled" -msgstr "Desabilitado" - -msgid "Enable" -msgstr "Ativar" - -msgid "Enabled" -msgstr "Ativado" - -msgid "Encrypt Method" -msgstr "Método de Cifragem" - -msgid "Forwarded IP" -msgstr "Endereço IP Encaminhado" - -msgid "Forwarding Tunnel" -msgstr "Tunel para Encaminhamento" - -msgid "Global Setting" -msgstr "Opções Globais" - -msgid "Ignore List" -msgstr "Lista de Ignorados" - -msgid "LAN" -msgstr "LAN" - -msgid "LAN IP List" -msgstr "Lista de endereços IP da LAN" - -msgid "Local Port" -msgstr "Porta Local" - -msgid "Password" -msgstr "Senha" - -msgid "Relay Mode" -msgstr "Modo de Retransmissor" - -msgid "Server Address" -msgstr "Endereço do Servidor" - -msgid "Server Port" -msgstr "Porta do servidor" - -msgid "ShadowSocks-libev" -msgstr "ShadowSocks-libev" - -msgid "ShadowSocks-libev is not running" -msgstr "O serviço ShadowSocks-libev está parado" - -msgid "ShadowSocks-libev is running" -msgstr "O serviço ShadowSocks-libev está em execução." - -msgid "UDP Forward" -msgstr "Encaminhamento UDP" - -msgid "UDP Local Port" -msgstr "Porta Local UDP" - -msgid "UDP Relay" -msgstr "Retransmissão UDP" - -msgid "WAN" -msgstr "WAN" diff --git a/applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po deleted file mode 100644 index b0cf6d3316..0000000000 --- a/applications/luci-app-shadowsocks-libev/po/sv/shadowsocks-libev.po +++ /dev/null @@ -1,136 +0,0 @@ -msgid "" -msgstr "" -"Content-Type: text/plain; charset=UTF-8\n" -"Project-Id-Version: PACKAGE VERSION\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" -"Language: sv\n" -"MIME-Version: 1.0\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -msgid "Access Control" -msgstr "" - -msgid "Allow all except listed" -msgstr "" - -msgid "Allow listed only" -msgstr "" - -msgid "Bypassed IP" -msgstr "" - -msgid "Connection Timeout" -msgstr "" - -msgid "Custom" -msgstr "" - -msgid "Disabled" -msgstr "" - -msgid "Enable" -msgstr "" - -msgid "Enabled" -msgstr "" - -msgid "Encrypt Method" -msgstr "" - -msgid "Forwarded IP" -msgstr "" - -msgid "Forwarding Tunnel" -msgstr "" - -msgid "Global Setting" -msgstr "" - -msgid "Ignore List" -msgstr "" - -msgid "LAN" -msgstr "" - -msgid "LAN IP List" -msgstr "" - -msgid "Local Port" -msgstr "" - -msgid "Password" -msgstr "" - -msgid "Relay Mode" -msgstr "" - -msgid "Server Address" -msgstr "" - -msgid "Server Port" -msgstr "" - -msgid "ShadowSocks-libev" -msgstr "" - -msgid "ShadowSocks-libev is not running" -msgstr "" - -msgid "ShadowSocks-libev is running" -msgstr "" - -msgid "UDP Forward" -msgstr "" - -msgid "UDP Local Port" -msgstr "" - -msgid "UDP Relay" -msgstr "" - -msgid "WAN" -msgstr "" - -#~ msgid "Broadcast on all interfaces" -#~ msgstr "Sänd i alla gränssnitt" - -#~ msgid "Choose the host to wake up or enter a custom MAC address to use" -#~ msgstr "" -#~ "Välj värden som ska väckas upp eller fyll i en anpassad MAC-adress att " -#~ "använda" - -#~ msgid "Host to wake up" -#~ msgstr "Värd som ska väckas upp" - -#~ msgid "Network interface to use" -#~ msgstr "Nätverksgränssnitt som ska användas" - -#~ msgid "" -#~ "Sometimes only one of the two tools works. If one fails, try the other one" -#~ msgstr "" -#~ "Ibland så fungerar bara en av de två verktygen. Prova med den andra om " -#~ "den första misslyckades" - -#~ msgid "Specifies the interface the WoL packet is sent on" -#~ msgstr "Anger gränssnittet som fjärrstartspaketet skickas med" - -#~ msgid "Starting WoL utility:" -#~ msgstr "Startar hjälpprogrammet för fjärrstyrning av uppstart:" - -#~ msgid "Wake on LAN" -#~ msgstr "Fjärrstyrning av uppstart" - -#~ msgid "" -#~ "Wake on LAN is a mechanism to remotely boot computers in the local " -#~ "network." -#~ msgstr "" -#~ "Fjärrstyrning av uppstart är en mekanism för att starta upp datorer via " -#~ "fjärrstyrning i det lokala nätverket." - -#~ msgid "Wake up host" -#~ msgstr "Väck upp värden" - -#~ msgid "WoL program" -#~ msgstr "Program för fjärrstart" diff --git a/applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot b/applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot deleted file mode 100644 index 81bbcb72f4..0000000000 --- a/applications/luci-app-shadowsocks-libev/po/templates/shadowsocks-libev.pot +++ /dev/null @@ -1,86 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" - -msgid "Access Control" -msgstr "" - -msgid "Allow all except listed" -msgstr "" - -msgid "Allow listed only" -msgstr "" - -msgid "Bypassed IP" -msgstr "" - -msgid "Connection Timeout" -msgstr "" - -msgid "Custom" -msgstr "" - -msgid "Disabled" -msgstr "" - -msgid "Enable" -msgstr "" - -msgid "Enabled" -msgstr "" - -msgid "Encrypt Method" -msgstr "" - -msgid "Forwarded IP" -msgstr "" - -msgid "Forwarding Tunnel" -msgstr "" - -msgid "Global Setting" -msgstr "" - -msgid "Ignore List" -msgstr "" - -msgid "LAN" -msgstr "" - -msgid "LAN IP List" -msgstr "" - -msgid "Local Port" -msgstr "" - -msgid "Password" -msgstr "" - -msgid "Relay Mode" -msgstr "" - -msgid "Server Address" -msgstr "" - -msgid "Server Port" -msgstr "" - -msgid "ShadowSocks-libev" -msgstr "" - -msgid "ShadowSocks-libev is not running" -msgstr "" - -msgid "ShadowSocks-libev is running" -msgstr "" - -msgid "UDP Forward" -msgstr "" - -msgid "UDP Local Port" -msgstr "" - -msgid "UDP Relay" -msgstr "" - -msgid "WAN" -msgstr "" diff --git a/applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po b/applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po deleted file mode 100644 index f86eee7e9f..0000000000 --- a/applications/luci-app-shadowsocks-libev/po/zh-cn/shadowsocks-libev.po +++ /dev/null @@ -1,97 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-11-12 14:12+0800\n" -"PO-Revision-Date: 2015-07-02 14:26+0800\n" -"Last-Translator: Jian Chang <aa65535@live.com>\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Pootle 2.0.6\n" - -msgid "Access Control" -msgstr "访问控制" - -msgid "Allow all except listed" -msgstr "仅允许列表外" - -msgid "Allow listed only" -msgstr "仅允许列表内" - -msgid "Bypassed IP" -msgstr "被忽略的IP" - -msgid "Connection Timeout" -msgstr "连接超时" - -msgid "Custom" -msgstr "自定义" - -msgid "Disabled" -msgstr "已禁用" - -msgid "Enable" -msgstr "启用" - -msgid "Enabled" -msgstr "已启用" - -msgid "Encrypt Method" -msgstr "加密方式" - -msgid "Forwarded IP" -msgstr "走代理的IP" - -msgid "Forwarding Tunnel" -msgstr "UDP转发地址" - -msgid "Global Setting" -msgstr "全局设置" - -msgid "Ignore List" -msgstr "忽略列表" - -msgid "LAN" -msgstr "" - -msgid "LAN IP List" -msgstr "内网IP列表" - -msgid "Local Port" -msgstr "本地端口" - -msgid "Password" -msgstr "密码" - -msgid "Relay Mode" -msgstr "中继模式" - -msgid "Server Address" -msgstr "服务器地址" - -msgid "Server Port" -msgstr "服务器端口" - -msgid "ShadowSocks-libev" -msgstr "ShadowSocks-libev" - -msgid "ShadowSocks-libev is not running" -msgstr "ShadowSocks-libev 未运行" - -msgid "ShadowSocks-libev is running" -msgstr "ShadowSocks-libev 运行中" - -msgid "UDP Forward" -msgstr "UDP转发" - -msgid "UDP Local Port" -msgstr "UDP本地端口" - -msgid "UDP Relay" -msgstr "UDP中继" - -msgid "WAN" -msgstr "" diff --git a/applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua b/applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua index 04eee93051..2a8aceec08 100644 --- a/applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua +++ b/applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/apcups.lua @@ -5,18 +5,34 @@ module("luci.statistics.rrdtool.definitions.apcups",package.seeall) function rrdargs( graph, plugin, plugin_instance, dtype ) + local voltagesdc = { + title = "%H: Voltages on APC UPS - Battery", + vlabel = "Volts DC", + alt_autoscale = true, + number_format = "%5.1lfV", + data = { + instances = { + voltage = { "battery" } + }, + + options = { + voltage = { title = "Battery voltage", noarea=true } + } + } + } + local voltages = { - title = "%H: Voltages on APC UPS ", - vlabel = "V", + title = "%H: Voltages on APC UPS - AC", + vlabel = "Volts AC", + alt_autoscale = true, number_format = "%5.1lfV", data = { instances = { - voltage = { "battery", "input", "output" } + voltage = { "input", "output" } }, options = { voltage_output = { color = "00e000", title = "Output voltage", noarea=true, overlay=true }, - voltage_battery = { color = "0000ff", title = "Battery voltage", noarea=true, overlay=true }, voltage_input = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true } } } @@ -97,5 +113,5 @@ function rrdargs( graph, plugin, plugin_instance, dtype ) } } - return { voltages, percentload, charge_percent, temperature, timeleft, frequency } + return { voltages, voltagesdc, percentload, charge_percent, temperature, timeleft, frequency } end diff --git a/applications/luci-app-statistics/po/ca/statistics.po b/applications/luci-app-statistics/po/ca/statistics.po index 33d5051f44..738af5510f 100644 --- a/applications/luci-app-statistics/po/ca/statistics.po +++ b/applications/luci-app-statistics/po/ca/statistics.po @@ -15,6 +15,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Acció (objectiu)" @@ -295,6 +301,9 @@ msgstr "Monitoritza els discs i les particions" msgid "Monitor filesystem types" msgstr "Monitoritza els tipus de sistema de fitxers" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Monitoritza màquines" @@ -381,6 +390,9 @@ msgstr "Configuració del connector ping" msgid "Port" msgstr "Port" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Processos" @@ -506,6 +518,9 @@ msgstr "TTL per paquets ping" msgid "Table" msgstr "Taula" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/cs/statistics.po b/applications/luci-app-statistics/po/cs/statistics.po index 849831607c..fc3f513c5c 100644 --- a/applications/luci-app-statistics/po/cs/statistics.po +++ b/applications/luci-app-statistics/po/cs/statistics.po @@ -11,6 +11,12 @@ msgstr "" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Akce (cíl)" @@ -290,6 +296,9 @@ msgstr "Sledovat disky a oddíly" msgid "Monitor filesystem types" msgstr "Sledovat typy souborových systémů" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Sledovat hostitele" @@ -376,6 +385,9 @@ msgstr "Nastavení pluginu Ping" msgid "Port" msgstr "Port" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Procesy" @@ -500,6 +512,9 @@ msgstr "TTL pro pakety pingu" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/de/statistics.po b/applications/luci-app-statistics/po/de/statistics.po index 196433e503..45ba020ada 100644 --- a/applications/luci-app-statistics/po/de/statistics.po +++ b/applications/luci-app-statistics/po/de/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Aktion (Ziel)" @@ -297,6 +303,9 @@ msgstr "Geräte und Partitionen überwachen" msgid "Monitor filesystem types" msgstr "Datesystemtypen überwachen" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Hosts überwachen" @@ -383,6 +392,9 @@ msgstr "Ping Plugin Konfiguration" msgid "Port" msgstr "Port" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Prozesse" @@ -506,6 +518,9 @@ msgstr "TTL für Ping Pakete" msgid "Table" msgstr "Tabelle" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" "Das NUT-Plugin liest Informationen über Unterbrechungsfreie Stromversorgungen" diff --git a/applications/luci-app-statistics/po/el/statistics.po b/applications/luci-app-statistics/po/el/statistics.po index da54cdac6c..4062868446 100644 --- a/applications/luci-app-statistics/po/el/statistics.po +++ b/applications/luci-app-statistics/po/el/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.4\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -288,6 +294,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -374,6 +383,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Διεργασίες" @@ -497,6 +509,9 @@ msgstr "" msgid "Table" msgstr "Πίνακας" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/en/statistics.po b/applications/luci-app-statistics/po/en/statistics.po index d9ab59ce0a..f7ebfe0c2b 100644 --- a/applications/luci-app-statistics/po/en/statistics.po +++ b/applications/luci-app-statistics/po/en/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Translate Toolkit 1.1.1\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Action (target)" @@ -293,6 +299,9 @@ msgstr "Monitor disks and partitions" msgid "Monitor filesystem types" msgstr "Monitor filesystem types" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Monitor hosts" @@ -379,6 +388,9 @@ msgstr "Ping Plugin Configuration" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Processes" @@ -502,6 +514,9 @@ msgstr "TTL for ping packets" msgid "Table" msgstr "Table" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/es/statistics.po b/applications/luci-app-statistics/po/es/statistics.po index 18c25819a6..3c811ffeff 100644 --- a/applications/luci-app-statistics/po/es/statistics.po +++ b/applications/luci-app-statistics/po/es/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Acción (objetivo)" @@ -292,6 +298,9 @@ msgstr "Monitorizar discos y particiones" msgid "Monitor filesystem types" msgstr "Monitorizar tipos de sistema de archivos" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Monitorizar máquinas" @@ -378,6 +387,9 @@ msgstr "Configuración del plugin \"Ping\"" msgid "Port" msgstr "Puerto" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Procesos" @@ -501,6 +513,9 @@ msgstr "TTL para paquetes de ping" msgid "Table" msgstr "Tabla" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" "El plugin NUT obtiene información sobre Sistemas de Alimentación " diff --git a/applications/luci-app-statistics/po/fr/statistics.po b/applications/luci-app-statistics/po/fr/statistics.po index b657bd38f0..bc156dd42a 100644 --- a/applications/luci-app-statistics/po/fr/statistics.po +++ b/applications/luci-app-statistics/po/fr/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Pootle 2.0.4\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Action (cible)" @@ -294,6 +300,9 @@ msgstr "Disques et partitions à surveiller" msgid "Monitor filesystem types" msgstr "types de systèmes de fichier à surveiller" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Hôtes à surveiller" @@ -380,6 +389,9 @@ msgstr "Configuration du greffon Ping" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Processus" @@ -503,6 +515,9 @@ msgstr "TTL des paquets ping" msgid "Table" msgstr "Table" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/he/statistics.po b/applications/luci-app-statistics/po/he/statistics.po index 6f40a47a24..35f978ed20 100644 --- a/applications/luci-app-statistics/po/he/statistics.po +++ b/applications/luci-app-statistics/po/he/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -283,6 +289,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -369,6 +378,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -492,6 +504,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/hu/statistics.po b/applications/luci-app-statistics/po/hu/statistics.po index 979c72f0f8..e5c4e601cb 100644 --- a/applications/luci-app-statistics/po/hu/statistics.po +++ b/applications/luci-app-statistics/po/hu/statistics.po @@ -11,6 +11,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Tevékenység (cél)" @@ -295,6 +301,9 @@ msgstr "Lemezek és partíciók figyelése" msgid "Monitor filesystem types" msgstr "Fájlrendszer típusok figyelése" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Gépek figyelése" @@ -381,6 +390,9 @@ msgstr "Ping bővítmény beállítása" msgid "Port" msgstr "Port" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Folyamatok" @@ -508,6 +520,9 @@ msgstr "TTL a ping csomagokhoz" msgid "Table" msgstr "Táblázat" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "A NUT bővítmény a szünetmentes tápokról ad információkat." diff --git a/applications/luci-app-statistics/po/it/statistics.po b/applications/luci-app-statistics/po/it/statistics.po index b0ae3d6b76..2451503f23 100644 --- a/applications/luci-app-statistics/po/it/statistics.po +++ b/applications/luci-app-statistics/po/it/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Azione (destinazione)" @@ -293,6 +299,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -379,6 +388,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -502,6 +514,9 @@ msgstr "" msgid "Table" msgstr "Tabella" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/ja/statistics.po b/applications/luci-app-statistics/po/ja/statistics.po index 690d9207d7..53941cf0f8 100644 --- a/applications/luci-app-statistics/po/ja/statistics.po +++ b/applications/luci-app-statistics/po/ja/statistics.po @@ -13,6 +13,12 @@ msgstr "" "X-Generator: Poedit 1.8.11\n" "Language-Team: \n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "アクション(対象)" @@ -295,6 +301,9 @@ msgstr "ディスクとパーティションをモニターする" msgid "Monitor filesystem types" msgstr "ファイルシステム タイプをモニターする" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "ホストをモニターする" @@ -384,6 +393,9 @@ msgstr "Ping プラグイン設定" msgid "Port" msgstr "ポート" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "プロセス" @@ -507,6 +519,9 @@ msgstr "pingパケットのTTL" msgid "Table" msgstr "テーブル" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "NUT プラグインは、無停電電源装置についての情報を読み取ります。" diff --git a/applications/luci-app-statistics/po/ms/statistics.po b/applications/luci-app-statistics/po/ms/statistics.po index 582314c545..c02556fc86 100644 --- a/applications/luci-app-statistics/po/ms/statistics.po +++ b/applications/luci-app-statistics/po/ms/statistics.po @@ -10,6 +10,12 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -280,6 +286,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -366,6 +375,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -489,6 +501,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/no/statistics.po b/applications/luci-app-statistics/po/no/statistics.po index d37bc488f5..4de2ee6b55 100644 --- a/applications/luci-app-statistics/po/no/statistics.po +++ b/applications/luci-app-statistics/po/no/statistics.po @@ -4,6 +4,12 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Handling (mål)" @@ -282,6 +288,9 @@ msgstr "Overvåk disker og partisjoner" msgid "Monitor filesystem types" msgstr "Overvåk filsystem typer" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Overvåk verter" @@ -368,6 +377,9 @@ msgstr "Ping plugin konfigurasjon" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Prosesser" @@ -491,6 +503,9 @@ msgstr "TTL for ping pakker" msgid "Table" msgstr "Tabell" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/pl/statistics.po b/applications/luci-app-statistics/po/pl/statistics.po index bf2ec93516..6e34ce0725 100644 --- a/applications/luci-app-statistics/po/pl/statistics.po +++ b/applications/luci-app-statistics/po/pl/statistics.po @@ -14,6 +14,12 @@ msgstr "" "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Akcja (cel)" @@ -296,6 +302,9 @@ msgstr "Monitoruj dyski i partycje" msgid "Monitor filesystem types" msgstr "Monitoruj system plików" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Monitoruj hosty" @@ -382,6 +391,9 @@ msgstr "Konfiguracja wtyczki Ping" msgid "Port" msgstr "Port" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Procesy" @@ -506,6 +518,9 @@ msgstr "TTL dla pakietów ping" msgid "Table" msgstr "Tabela" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "Wtyczka Nut Informuje o Nie przerywalnym Zasilaniu" diff --git a/applications/luci-app-statistics/po/pt-br/statistics.po b/applications/luci-app-statistics/po/pt-br/statistics.po index 74c4a2603f..c5d6899332 100644 --- a/applications/luci-app-statistics/po/pt-br/statistics.po +++ b/applications/luci-app-statistics/po/pt-br/statistics.po @@ -13,6 +13,12 @@ msgstr "" "X-Generator: Poedit 1.8.11\n" "Language-Team: \n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Ação (destino)" @@ -299,6 +305,9 @@ msgstr "Monitoras discos e partições" msgid "Monitor filesystem types" msgstr "Monitorar tipos de sistemas de arquivos" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Monitorar os equipamentos" @@ -388,6 +397,9 @@ msgstr "Configuração do plugin Ping" msgid "Port" msgstr "Porta" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Processos" @@ -511,6 +523,9 @@ msgstr "TTL para os pacotes do ping" msgid "Table" msgstr "Tabela" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "O plugin NUT lê informações sobre Fontes de alimentação ininterruptas." diff --git a/applications/luci-app-statistics/po/pt/statistics.po b/applications/luci-app-statistics/po/pt/statistics.po index 79c7bd03e6..245e6e9bf9 100644 --- a/applications/luci-app-statistics/po/pt/statistics.po +++ b/applications/luci-app-statistics/po/pt/statistics.po @@ -13,6 +13,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Ação (destino)" @@ -295,6 +301,9 @@ msgstr "Monitoras discos e partições" msgid "Monitor filesystem types" msgstr "Monitorar tipos de sistemas de arquivos" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Monitorar os hosts" @@ -381,6 +390,9 @@ msgstr "Configuração do plugin Ping" msgid "Port" msgstr "Porta" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Processos" @@ -504,6 +516,9 @@ msgstr "TTL para os pacotes do ping" msgid "Table" msgstr "Tabela" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/ro/statistics.po b/applications/luci-app-statistics/po/ro/statistics.po index c5dfcfe558..a326fec799 100644 --- a/applications/luci-app-statistics/po/ro/statistics.po +++ b/applications/luci-app-statistics/po/ro/statistics.po @@ -14,6 +14,12 @@ msgstr "" "20)) ? 1 : 2);;\n" "X-Generator: Pootle 2.0.4\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -287,6 +293,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -373,6 +382,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Procese" @@ -496,6 +508,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/ru/statistics.po b/applications/luci-app-statistics/po/ru/statistics.po index 3a418dec75..9d0ff9fdf2 100644 --- a/applications/luci-app-statistics/po/ru/statistics.po +++ b/applications/luci-app-statistics/po/ru/statistics.po @@ -15,6 +15,12 @@ msgstr "" "X-Generator: Pootle 2.0.6\n" "X-Poedit-SourceCharset: UTF-8\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Действие (цель)" @@ -297,6 +303,9 @@ msgstr "Собирать статистику с дисков и раздело msgid "Monitor filesystem types" msgstr "Собирать статистику с файловых систем" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Собирать статистику с хостов" @@ -383,6 +392,9 @@ msgstr "Конфигурация модуля Ping" msgid "Port" msgstr "Порт" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Процессы" @@ -508,6 +520,9 @@ msgstr "TTL для ping-пакетов" msgid "Table" msgstr "Таблица" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/sk/statistics.po b/applications/luci-app-statistics/po/sk/statistics.po index 6dba7d09b8..53858ca540 100644 --- a/applications/luci-app-statistics/po/sk/statistics.po +++ b/applications/luci-app-statistics/po/sk/statistics.po @@ -8,6 +8,12 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -278,6 +284,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -364,6 +373,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -487,6 +499,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/sv/statistics.po b/applications/luci-app-statistics/po/sv/statistics.po index bef0f2d6c9..9d738f2f33 100644 --- a/applications/luci-app-statistics/po/sv/statistics.po +++ b/applications/luci-app-statistics/po/sv/statistics.po @@ -9,6 +9,12 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -283,6 +289,9 @@ msgstr "Övervaka hårddiskar och partitioner" msgid "Monitor filesystem types" msgstr "Övervaka filsystemtyper" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Övervaka värdar" @@ -369,6 +378,9 @@ msgstr "" msgid "Port" msgstr "Port" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Processer" @@ -492,6 +504,9 @@ msgstr "TTL för ping-paket" msgid "Table" msgstr "Tabell" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/templates/statistics.pot b/applications/luci-app-statistics/po/templates/statistics.pot index c57a85b76a..ec630b6962 100644 --- a/applications/luci-app-statistics/po/templates/statistics.pot +++ b/applications/luci-app-statistics/po/templates/statistics.pot @@ -1,6 +1,12 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -271,6 +277,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -357,6 +366,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -480,6 +492,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/tr/statistics.po b/applications/luci-app-statistics/po/tr/statistics.po index 6d7056f3b7..860ff95e88 100644 --- a/applications/luci-app-statistics/po/tr/statistics.po +++ b/applications/luci-app-statistics/po/tr/statistics.po @@ -9,6 +9,12 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -279,6 +285,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -365,6 +374,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -488,6 +500,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/uk/statistics.po b/applications/luci-app-statistics/po/uk/statistics.po index de17a3caf8..ac9ae50940 100644 --- a/applications/luci-app-statistics/po/uk/statistics.po +++ b/applications/luci-app-statistics/po/uk/statistics.po @@ -14,6 +14,12 @@ msgstr "" "10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Pootle 2.0.6\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -284,6 +290,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -370,6 +379,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -493,6 +505,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/vi/statistics.po b/applications/luci-app-statistics/po/vi/statistics.po index bdb7f1a3a1..f5798a2651 100644 --- a/applications/luci-app-statistics/po/vi/statistics.po +++ b/applications/luci-app-statistics/po/vi/statistics.po @@ -14,6 +14,12 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Pootle 1.1.0\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "Action (target)" @@ -294,6 +300,9 @@ msgstr "Kiểm soát đĩa và phân vùng" msgid "Monitor filesystem types" msgstr "Kiểm soát loại filesystem" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "Monitor hosts" @@ -380,6 +389,9 @@ msgstr "Cấu hình Ping plugin" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "Quá trình xử lý" @@ -503,6 +515,9 @@ msgstr "TTl cho gói ping" msgid "Table" msgstr "Table" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-statistics/po/zh-cn/statistics.po b/applications/luci-app-statistics/po/zh-cn/statistics.po index 46cf59feed..20f5a93cb4 100644 --- a/applications/luci-app-statistics/po/zh-cn/statistics.po +++ b/applications/luci-app-statistics/po/zh-cn/statistics.po @@ -13,6 +13,12 @@ msgstr "" "X-Generator: Poedit 2.0.1\n" "Language-Team: \n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "动作(目标)" @@ -289,6 +295,9 @@ msgstr "监测磁盘和分区" msgid "Monitor filesystem types" msgstr "监测文件系统类型" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "监测主机" @@ -377,6 +386,9 @@ msgstr "Ping插件配置" msgid "Port" msgstr "端口" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "进程" @@ -500,6 +512,9 @@ msgstr "ping包TTL" msgid "Table" msgstr "表" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "NUT插件读取UPS信息。" diff --git a/applications/luci-app-statistics/po/zh-tw/statistics.po b/applications/luci-app-statistics/po/zh-tw/statistics.po index cbd6d9d38e..36e42c1d09 100644 --- a/applications/luci-app-statistics/po/zh-tw/statistics.po +++ b/applications/luci-app-statistics/po/zh-tw/statistics.po @@ -7,6 +7,12 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Transfer-Encoding: 8bit\n" +msgid "APC UPS" +msgstr "" + +msgid "APCUPS Plugin Configuration" +msgstr "" + msgid "Action (target)" msgstr "" @@ -277,6 +283,9 @@ msgstr "" msgid "Monitor filesystem types" msgstr "" +msgid "Monitor host" +msgstr "" + msgid "Monitor hosts" msgstr "" @@ -363,6 +372,9 @@ msgstr "" msgid "Port" msgstr "" +msgid "Port for apcupsd communication" +msgstr "" + msgid "Processes" msgstr "" @@ -486,6 +498,9 @@ msgstr "" msgid "Table" msgstr "" +msgid "The APCUPS plugin collects statistics about the APC UPS." +msgstr "" + msgid "The NUT plugin reads information about Uninterruptible Power Supplies." msgstr "" diff --git a/applications/luci-app-travelmate/luasrc/controller/travelmate.lua b/applications/luci-app-travelmate/luasrc/controller/travelmate.lua index 86382f6ae0..a418a8ec61 100644 --- a/applications/luci-app-travelmate/luasrc/controller/travelmate.lua +++ b/applications/luci-app-travelmate/luasrc/controller/travelmate.lua @@ -5,8 +5,8 @@ module("luci.controller.travelmate", package.seeall) local fs = require("nixio.fs") local util = require("luci.util") -local template = require("luci.template") local i18n = require("luci.i18n") +local templ = require("luci.template") function index() if not nixio.fs.access("/etc/config/travelmate") then @@ -14,15 +14,22 @@ function index() end entry({"admin", "services", "travelmate"}, firstchild(), _("Travelmate"), 40).dependent = false entry({"admin", "services", "travelmate", "tab_from_cbi"}, cbi("travelmate/overview_tab", {hideresetbtn=true, hidesavebtn=true}), _("Overview"), 10).leaf = true - entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 20).leaf = true + entry({"admin", "services", "travelmate", "stations"}, template("travelmate/stations"), _("Wireless Stations"), 20).leaf = true + entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 30).leaf = true entry({"admin", "services", "travelmate", "advanced"}, firstchild(), _("Advanced"), 100) entry({"admin", "services", "travelmate", "advanced", "configuration"}, cbi("travelmate/configuration_tab"), _("Edit Travelmate Configuration"), 110).leaf = true entry({"admin", "services", "travelmate", "advanced", "cfg_wireless"}, cbi("travelmate/cfg_wireless_tab"), _("Edit Wireless Configuration"), 120).leaf = true entry({"admin", "services", "travelmate", "advanced", "cfg_network"}, cbi("travelmate/cfg_network_tab"), _("Edit Network Configuration"), 130).leaf = true entry({"admin", "services", "travelmate", "advanced", "cfg_firewall"}, cbi("travelmate/cfg_firewall_tab"), _("Edit Firewall Configuration"), 140).leaf = true + + entry({"admin", "services", "travelmate", "wifiscan"}, template("travelmate/wifi_scan")).leaf = true + entry({"admin", "services", "travelmate", "wifiadd"}, cbi("travelmate/wifi_add", {hideresetbtn=true, hidesavebtn=true})).leaf = true + entry({"admin", "services", "travelmate", "wifiedit"}, cbi("travelmate/wifi_edit", {hideresetbtn=true, hidesavebtn=true})).leaf = true + entry({"admin", "services", "travelmate", "wifidelete"}, cbi("travelmate/wifi_delete", {hideresetbtn=true, hidesavebtn=true})).leaf = true + entry({"admin", "services", "travelmate", "wifiorder"}, cbi("travelmate/wifi_order", {hideresetbtn=true, hidesavebtn=true})).leaf = true end function logread() local logfile = util.trim(util.exec("logread -e 'travelmate'")) - template.render("travelmate/logread", {title = i18n.translate("Travelmate Logfile"), content = logfile}) + templ.render("travelmate/logread", {title = i18n.translate("Travelmate Logfile"), content = logfile}) end diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua index 236bbb0de5..12cb72cbe5 100644 --- a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua +++ b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua @@ -2,12 +2,13 @@ -- This is free software, licensed under the Apache License, Version 2.0 local fs = require("nixio.fs") -local uci = require("uci") +local uci = require("luci.model.uci").cursor() local json = require("luci.jsonc") local nw = require("luci.model.network").init() local fw = require("luci.model.firewall").init() -local uplink = uci.get("network", "trm_wwan") or "" +local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan" local trminput = uci.get("travelmate", "global", "trm_rtfile") or "/tmp/trm_runtime.json" +local uplink = uci.get("network", trmiface) or "" local parse = json.parse(fs.readfile(trminput) or "") m = Map("travelmate", translate("Travelmate"), @@ -21,70 +22,87 @@ function m.on_after_commit(self) luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate")) end --- Main travelmate options - s = m:section(NamedSection, "global", "travelmate") -o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate")) -o1.default = o1.disabled -o1.rmempty = false +-- Interface Wizard -o2 = s:option(Flag, "trm_automatic", translate("Enable 'automatic' mode"), - translate("Keep travelmate in an active state.")) -o2.default = o2.enabled -o2.rmempty = false - -o3 = s:option(Value, "trm_iface", translate("Restrict interface trigger to certain interface(s)"), - translate("Space separated list of interfaces that trigger travelmate processing. ".. - "To disable event driven (re-)starts remove all entries.")) -o3.rmempty = true - -o4 = s:option(Value, "trm_triggerdelay", translate("Trigger delay"), - translate("Additional trigger delay in seconds before travelmate processing begins.")) -o4.default = 2 -o4.datatype = "range(1,90)" -o4.rmempty = false +if uplink == "" then + dv = s:option(DummyValue, "nil", translate("Interface Wizard")) + dv.template = "cbi/nullsection" -o5 = s:option(Flag, "trm_debug", translate("Enable verbose debug logging")) -o5.default = o5.disabled -o5.rmempty = false + o = s:option(Value, "trm_iface", translate("Uplink interface")) + o.datatype = "and(uciname,rangelength(3,15))" + o.default = "trm_wwan" + o.rmempty = false --- Interface setup + function o.write(self, section, value) + iface = o:formvalue(section) + uci:set("travelmate", section, "trm_iface", iface) + uci:save("travelmate") + uci:commit("travelmate") + end -if uplink == "" then - dv = s:option(DummyValue, "_dummy", translate("Interface Setup")) - dv.template = "cbi/nullsection" btn = s:option(Button, "", translate("Create Uplink Interface"), - translate("Automatically create a new wireless wan uplink interface 'trm_wwan', configure it to use dhcp and ") + translate("Create a new wireless wan uplink interface, configure it to use dhcp and ") .. translate("add it to the wan zone of the firewall. This step has only to be done once.")) btn.inputtitle = translate("Add Interface") btn.inputstyle = "apply" btn.disabled = false function btn.write() - local name = "trm_wwan" - local net = nw:add_network(name, { proto = "dhcp" }) + local net = nw:add_network(iface, { proto = "dhcp" }) if net then nw:save("network") nw:commit("network") local zone = fw:get_zone_by_network("wan") if zone then - zone:add_network(name) + zone:add_network(iface) fw:save("firewall") fw:commit("firewall") + luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1") end - luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1") - luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate")) end + luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate")) end -else - dv = s:option(DummyValue, "_dummy", translate("Interface Setup"), - translate("<br /> Network Interface 'trm_wwan' created successfully. ") - .. translatef("Scan & Add new wireless stations via standard " - .. "<a href=\"%s\">" - .. "Wireless Setup</a>", luci.dispatcher.build_url("admin/network/wireless"))) - dv.template = "cbi/nullsection" + return m end +-- Main travelmate options + +o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate")) +o1.default = o1.disabled +o1.rmempty = false + +o2 = s:option(Flag, "trm_automatic", translate("Enable 'automatic' mode"), + translate("Keep travelmate in an active state. Check every n seconds the connection status, i.e. the uplink availability.")) +o2.default = o2.enabled +o2.rmempty = false + +btn = s:option(Button, "", translate("Manual Rescan")) +btn:depends("trm_automatic", "") +btn.inputtitle = translate("Rescan") +btn.inputstyle = "find" +btn.disabled = false +function btn.write() + luci.sys.call("/etc/init.d/travelmate start >/dev/null 2>&1") + luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate")) +end + +o3 = s:option(Value, "trm_iface", translate("Uplink / Trigger interface"), + translate("Name of the uplink interface that triggers travelmate processing in 'manual' mode.")) +o3.datatype = "and(uciname,rangelength(3,15))" +o3.default = trmiface +o3.rmempty = false + +o4 = s:option(Value, "trm_triggerdelay", translate("Trigger delay"), + translate("Additional trigger delay in seconds before travelmate processing begins.")) +o4.default = 2 +o4.datatype = "range(1,90)" +o4.rmempty = false + +o5 = s:option(Flag, "trm_debug", translate("Enable verbose debug logging")) +o5.default = o5.disabled +o5.rmempty = false + -- Runtime information ds = s:option(DummyValue, "_dummy", translate("Runtime information")) diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua new file mode 100644 index 0000000000..979307e001 --- /dev/null +++ b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua @@ -0,0 +1,69 @@ +-- Copyright 2017 Dirk Brenken (dev@brenken.org) +-- This is free software, licensed under the Apache License, Version 2.0 + +local fs = require("nixio.fs") +local uci = require("luci.model.uci").cursor() +local http = require("luci.http") +local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan" + +m = SimpleForm("add", translate("Add Wireless Uplink Configuration")) +m.cancel = translate("Back to overview") +m.reset = false + +function m.on_cancel() + http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) +end + +m.hidden = { + device = http.formvalue("device"), + ssid = http.formvalue("ssid"), + wep = http.formvalue("wep"), + wpa_suites = http.formvalue("wpa_suites"), + wpa_version = http.formvalue("wpa_version") +} + +if m.hidden.ssid ~= "" then + wssid = m:field(Value, "ssid", translate("SSID")) + wssid.default = m.hidden.ssid +else + wssid = m:field(Value, "ssid", translate("SSID (hidden)")) +end + +if (tonumber(m.hidden.wep) or 0) == 1 then + wkey = m:field(Value, "key", translate("WEP passphrase"), + translate("Specify the secret encryption key here.")) + wkey.password = true + wkey.datatype = "wepkey" +elseif (tonumber(m.hidden.wpa_version) or 0) > 0 and + (m.hidden.wpa_suites == "PSK" or m.hidden.wpa_suites == "PSK2") +then + wkey = m:field(Value, "key", translate("WPA passphrase"), + translate("Specify the secret encryption key here.")) + wkey.password = true + wkey.datatype = "wpakey" +end + +function wssid.write(self, section, value) + newsection = uci:section("wireless", "wifi-iface", nil, { + mode = "sta", + network = trmiface, + device = m.hidden.device, + ssid = wssid:formvalue(section), + disabled = "1" + }) + if (tonumber(m.hidden.wep) or 0) == 1 then + uci:set("wireless", newsection, "encryption", "wep-open") + uci:set("wireless", newsection, "key", "1") + uci:set("wireless", newsection, "key1", wkey:formvalue(section)) + elseif (tonumber(m.hidden.wpa_version) or 0) > 0 then + uci:set("wireless", newsection, "encryption", "psk2") + uci:set("wireless", newsection, "key", wkey:formvalue(section)) + else + uci:set("wireless", newsection, "encryption", "none") + end + uci:save("wireless") + uci:commit("wireless") + http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) +end + +return m diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua new file mode 100644 index 0000000000..97ec1ca3ca --- /dev/null +++ b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_delete.lua @@ -0,0 +1,14 @@ +-- Copyright 2017 Dirk Brenken (dev@brenken.org) +-- This is free software, licensed under the Apache License, Version 2.0 + +local uci = require("luci.model.uci").cursor() +local http = require("luci.http") +local cfg = http.formvalue("cfg") + +if cfg ~= nil then + uci:delete("wireless", cfg) + uci:save("wireless") + uci:commit("wireless") +end + +http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua new file mode 100644 index 0000000000..0bae98460f --- /dev/null +++ b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua @@ -0,0 +1,49 @@ +-- Copyright 2017 Dirk Brenken (dev@brenken.org) +-- This is free software, licensed under the Apache License, Version 2.0 + +local fs = require("nixio.fs") +local uci = require("luci.model.uci").cursor() +local http = require("luci.http") + +m = SimpleForm("edit", translate("Edit Wireless Uplink Configuration")) +m.cancel = translate("Back to overview") +m.reset = false + +function m.on_cancel() + http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) +end + +m.hidden = { + cfg = http.formvalue("cfg") +} + +local s = uci:get_all("wireless", m.hidden.cfg) +if s ~= nil then + wssid = m:field(Value, "ssid", translate("SSID")) + wssid.default = s.ssid + + if s.encryption and s.key then + wkey = m:field(Value, "key", translatef("Passphrase (%s)", s.encryption)) + wkey.password = true + wkey.default = s.key + if s.encryption == "wep" then + wkey.datatype = "wepkey" + else + wkey.datatype = "wpakey" + end + end +else + http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) +end + +function wssid.write(self, section, value) + uci:set("wireless", m.hidden.cfg, "ssid", wssid:formvalue(section)) + if s.encryption and s.key then + uci:set("wireless", m.hidden.cfg, "key", wkey:formvalue(section)) + end + uci:save("wireless") + uci:commit("wireless") + http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) +end + +return m diff --git a/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_order.lua b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_order.lua new file mode 100644 index 0000000000..d53e1f55e5 --- /dev/null +++ b/applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_order.lua @@ -0,0 +1,51 @@ +-- Copyright 2017 Dirk Brenken (dev@brenken.org) +-- This is free software, licensed under the Apache License, Version 2.0 + +local http = require("luci.http") +local cfg = http.formvalue("cfg") +local dir = http.formvalue("dir") +local uci = require("luci.model.uci").cursor() +local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan" + +if cfg ~= nil then + local iface = "" + local section = "" + local idx = "" + local idx_change = "" + if dir == "up" then + uci:foreach("wireless", "wifi-iface", function(s) + iface = s.network + if iface == trmiface then + section = s['.name'] + if cfg == section then + idx = s['.index'] + else + idx_change = s['.index'] + end + if idx ~= "" and idx_change ~= "" and idx_change < idx then + uci:reorder("wireless", cfg, idx_change) + idx = "" + end + end + end) + elseif dir == "down" then + uci:foreach("wireless", "wifi-iface", function(s) + iface = s.network + if iface == trmiface then + section = s['.name'] + if cfg == section then + idx = s['.index'] + else + idx_change = s['.index'] + end + if idx ~= "" and idx_change ~= "" and idx_change > idx then + uci:reorder("wireless", cfg, idx_change) + idx = "" + end + end + end) + end + uci:save("wireless") + uci:commit("wireless") +end +http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/runtime.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/runtime.htm index ee3a4553a8..2b9885567a 100644 --- a/applications/luci-app-travelmate/luasrc/view/travelmate/runtime.htm +++ b/applications/luci-app-travelmate/luasrc/view/travelmate/runtime.htm @@ -5,6 +5,6 @@ This is free software, licensed under the Apache License, Version 2.0 <%+cbi/valueheader%> -<input name="runtime" id="runtime" type="text" class="cbi-input-text" style="border: none; box-shadow: none; background-color: #ffffff; color: #0069d6;" value="<%=self:cfgvalue(section)%>" disabled="disabled" /> +<input name="runtime" id="runtime" type="text" class="cbi-input-text" style="border:none; box-shadow:none; background-color:#ffffff; color:#0069d6;" value="<%=self:cfgvalue(section)%>" disabled="disabled" /> <%+cbi/valuefooter%> diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm new file mode 100644 index 0000000000..f1f26801ce --- /dev/null +++ b/applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm @@ -0,0 +1,78 @@ +<%# +Copyright 2017 Dirk Brenken (dev@brenken.org) +This is free software, licensed under the Apache License, Version 2.0 +-%> + +<%- + local write = io.write + local uci = require "luci.model.uci".cursor() + local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan" +-%> + +<%+header%> + +<div class="cbi-map"> +<h2 name="content"><%:Wireless Stations%></h2> +<div class="cbi-map-descr"> + <%=translatef("Provides an overview of all configured uplinks for the travelmate interface (%s). You can edit, delete or re-order existing uplinks or scan for a new one. The currently used uplink is emphasized in blue.", trmiface)%> +</div> + +<fieldset class="cbi-section"> + <table class="cbi-section-table" style="empty-cells:hide"> + <tr class="cbi-section-table-titles"> + <th class="cbi-section-table-cell" style="text-align:left"><%:Device%></th> + <th class="cbi-section-table-cell" style="text-align:left"><%:SSID%></th> + <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th> + <th class="cbi-section-table-cell" style="text-align:center" colspan="2"><%:Actions%></th> + </tr> +<% + uci:foreach("wireless", "wifi-iface", function(s) + local iface = s.network or "" + if iface == trmiface then + local section = s['.name'] + local device = s.device or "" + local mode = s.mode or "" + local ssid = s.ssid or "" + local encryption = s.encryption or "" + local disabled = s.disabled or "" + local style = "color:#000000" + if disabled == "0" then + style = "color:#0069d6;font-weight:bold" + end +%> + <tr class="cbi-section-table-row cbi-rowstyle-1" style="<%=style%>"> + <td style="text-align:left"><%=device%></td> + <td style="text-align:left"><%=ssid%></td> + <td style="text-align:left"><%=encryption%></td> + <td class="cbi-value-field" style="width:70px;text-align:right"> + <input class="cbi-button cbi-button-up" type="button" value="" onclick="location.href='<%=url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>;dir=up'" alt="<%:Move up%>" title="<%:Move up%>"/> + <input class="cbi-button cbi-button-down" type="button" value="" onclick="location.href='<%=url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>;dir=down'" alt="<%:Move down%>" title="<%:Move down%>"/> + </td> + <td class="cbi-value-field" style="width:150px;text-align:right"> + <input type="button" class="cbi-button cbi-button-edit" onclick="location.href='<%=url('admin/services/travelmate/wifiedit')%>?cfg=<%=section%>'" title="<%:Edit this Uplink%>" value="<%:Edit%>"/> + <input type="button" class="cbi-button cbi-button-remove" onclick="location.href='<%=url('admin/services/travelmate/wifidelete')%>?cfg=<%=section%>'" title="<%:Delete this Uplink%>" value="<%:Delete%>"/> + </td> + </tr> +<% + end + end) +%> + </table> +</fieldset> +<div class="cbi-page-actions right"> +<% + uci:foreach("wireless", "wifi-device", function(s) + local device = s[".name"] +%> + <form class="inline" action="<%=url('admin/services/travelmate/wifiscan')%>" method="post"> + <input type="hidden" name="device" value="<%=device%>"/> + <input type="hidden" name="token" value="<%=token%>"/> + <input type="submit" class="cbi-button cbi-button-find" title="<%:Find and join network on %><%=device%>" value="<%:Scan %><%=device%>"/> + </form> +<% + end) +%> +</div> +</div> + +<%+footer%> diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm new file mode 100644 index 0000000000..dea107eef4 --- /dev/null +++ b/applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm @@ -0,0 +1,90 @@ +<%# +Copyright 2017 Dirk Brenken (dev@brenken.org) +This is free software, licensed under the Apache License, Version 2.0 +-%> + +<%- + local sys = require "luci.sys" + local utl = require "luci.util" + local dev = luci.http.formvalue("device") + local iw = luci.sys.wifi.getiwinfo(dev) + + if not iw then + luci.http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations")) + end + + function format_wifi_encryption(info) + if info.wep == true then + return translate("WEP") + elseif info.wpa > 0 then + return translate("WPA / WPA2") + elseif info.enabled then + return translate("Unknown") + else + return translate("Open") + end + end + + function percent_wifi_signal(info) + local qc = info.quality or 0 + local qm = info.quality_max or 0 + if info.bssid and qc > 0 and qm > 0 then + return math.floor((100 / qm) * qc) + else + return 0 + end + end +-%> + +<%+header%> + +<div class="cbi-map"> +<h2 name="content"><%:Wireless Scan%></h2> + <fieldset class="cbi-section"> + <table class="cbi-section-table" style="empty-cells:hide"> + <tr class="cbi-section-table-titles"> + <th class="cbi-section-table-cell" style="text-align:left"><%:Uplink SSID%></th> + <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th> + <th class="cbi-section-table-cell" style="text-align:left" colspan="2"><%:Signal strength%></th> + </tr> + <% for i, net in ipairs(iw.scanlist or { }) do%> + <tr class="cbi-section-table-row cbi-rowstyle-1"> + <td class="cbi-value-field" style="text-align:left"> + <strong><%=net.ssid and utl.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%></strong> + </td> + <td class="cbi-value-field" style="text-align:left"> + <%=format_wifi_encryption(net.encryption)%> + </td> + <td class="cbi-value-field" style="text-align:left"> + <%=percent_wifi_signal(net)%> % + </td> + <td class="cbi-value-field" style="width:100px;text-align:right"> + <form class="inline" action="<%=url('admin/services/travelmate/wifiadd')%>" method="post"> + <input type="hidden" name="token" value="<%=token%>"/> + <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/> + <input type="hidden" name="ssid" value="<%=utl.pcdata(net.ssid)%>"/> + <input type="hidden" name="wep" value="<%=net.encryption.wep and 1 or 0%>"/> + <% if net.encryption.wpa then %> + <input type="hidden" name="wpa_version" value="<%=net.encryption.wpa%>"/> + <% for _, v in ipairs(net.encryption.auth_suites) do %><input type="hidden" name="wpa_suites" value="<%=v%>"/> + <% end; end %> + <input class="cbi-button cbi-button-apply" type="submit" value="<%:Add Uplink%>"/> + </form> + </td> + </tr> + <% end %> + </table> + </fieldset> +<div class="cbi-page-actions right"> + <form class="inline" action="<%=url('admin/services/travelmate/stations')%>" method="post"> + <input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>"/> + </form> + <form class="inline" action="<%=url('admin/services/travelmate/wifiscan')%>" method="post"> + <input type="hidden" name="token" value="<%=token%>"/> + <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/> + <input class="cbi-button cbi-input-find" type="submit" value="<%:Repeat scan%>"/> + </form> +</div> +</div> + +<%+footer%> diff --git a/applications/luci-app-travelmate/po/ja/travelmate.po b/applications/luci-app-travelmate/po/ja/travelmate.po index e4a8b8bda2..150ef7047c 100644 --- a/applications/luci-app-travelmate/po/ja/travelmate.po +++ b/applications/luci-app-travelmate/po/ja/travelmate.po @@ -7,18 +7,23 @@ msgstr "" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.0.1\n" +"X-Generator: Poedit 2.0.3\n" "Last-Translator: INAGAKI Hiroshi <musashino.open@gmail.com>\n" "Plural-Forms: nplurals=1; plural=0;\n" "Language: ja\n" -msgid "<br /> Network Interface 'trm_wwan' created successfully." -msgstr "" -"<br /> ネットワーク インターフェース 'trm_wwan' の作成に成功しました。" +msgid "Actions" +msgstr "操作" msgid "Add Interface" msgstr "インターフェースの追加" +msgid "Add Uplink" +msgstr "アップリンクの追加" + +msgid "Add Wireless Uplink Configuration" +msgstr "無線アップリンク追加の設定" + msgid "" "Additional trigger delay in seconds before travelmate processing begins." msgstr "Travelmate の処理が開始されるまでの、追加の遅延時間(秒)です。" @@ -26,12 +31,8 @@ msgstr "Travelmate の処理が開始されるまでの、追加の遅延時間 msgid "Advanced" msgstr "詳細設定" -msgid "" -"Automatically create a new wireless wan uplink interface 'trm_wwan', " -"configure it to use dhcp and" -msgstr "" -"新しい無線 WAN インターフェース 'trm_wwan' を自動的に作成し、 DHCP を使用する" -"よう構成して" +msgid "Back to overview" +msgstr "概要へ戻る" msgid "" "Configuration of the travelmate package to to enable travel router " @@ -45,6 +46,24 @@ msgstr "接続制限" msgid "Create Uplink Interface" msgstr "アップリンク インターフェースの作成" +msgid "" +"Create a new wireless wan uplink interface, configure it to use dhcp and" +msgstr "" +"新規の無線 WAN アップリンク インターフェースを作成し、 DHCP を使用するよう構" +"成して" + +msgid "Delete" +msgstr "削除" + +msgid "Delete this Uplink" +msgstr "このアップリンクを削除" + +msgid "Device" +msgstr "デバイス" + +msgid "Edit" +msgstr "編集" + msgid "Edit Firewall Configuration" msgstr "ファイアウォール設定の編集" @@ -57,6 +76,12 @@ msgstr "Travelmate 設定の編集" msgid "Edit Wireless Configuration" msgstr "無線設定の編集" +msgid "Edit Wireless Uplink Configuration" +msgstr "無線アップリンク設定の編集" + +msgid "Edit this Uplink" +msgstr "このアップリンクを編集" + msgid "Enable 'automatic' mode" msgstr "'automatic' モードの有効化" @@ -66,15 +91,21 @@ msgstr "Travelmate の有効化" msgid "Enable verbose debug logging" msgstr "詳細なデバッグ ログの有効化" +msgid "Encryption" +msgstr "暗号化" + msgid "Extra options" msgstr "拡張オプション" +msgid "Find and join network on" +msgstr "ネットワークの検索と参加:" + msgid "" "For further information <a href=\"%s\" target=\"_blank\">see online " "documentation</a>" msgstr "" -"詳細な情報は <a href=\"%s\" target=\"_blank\">オンライン ドキュメント</a>を確" -"認してください。" +"詳細な情報は <a href=\"%s\" target=\"_blank\">オンライン ドキュメント</a> を" +"確認してください。" msgid "How long should travelmate wait for a successful wlan interface reload" msgstr "" @@ -82,26 +113,49 @@ msgstr "" "す。" msgid "How many times should travelmate try to connect to an Uplink" -msgstr "Travelmate がアップリンクに対して接続を試行する回数です。" +msgstr "Travelmate がアップリンクへの接続を試行する回数です。" msgid "Input file not found, please check your configuration." msgstr "入力ファイルが見つかりません。設定を確認してください。" -msgid "Interface Setup" -msgstr "インターフェース設定" - msgid "Interface Timeout" msgstr "インターフェース タイムアウト" -msgid "Keep travelmate in an active state." -msgstr "Travelmate をアクティブ状態で維持します。" +msgid "Interface Wizard" +msgstr "インターフェース ウィザード" + +msgid "" +"Keep travelmate in an active state. Check every n seconds the connection " +"status, i.e. the uplink availability." +msgstr "" +"Travelmate をアクティブ状態で維持します。 n秒ごとにアップリンクの可用性確認と" +"して、接続状態をチェックします" msgid "Last rundate" msgstr "最終実行日時" +msgid "Manual Rescan" +msgstr "手動再スキャン" + +msgid "Move down" +msgstr "下へ" + +msgid "Move up" +msgstr "上へ" + +msgid "" +"Name of the uplink interface that triggers travelmate processing in 'manual' " +"mode." +msgstr "" +"'manual' モード時に Travelmate の処理のトリガーとなる、アップリンク インター" +"フェースの名前です。" + msgid "Online Status" msgstr "オンライン ステータス" +msgid "Open" +msgstr "オープン" + msgid "" "Options for further tweaking in case the defaults are not suitable for you." msgstr "デフォルトの設定が適切でない場合、さらに設定するためのオプションです。" @@ -112,11 +166,27 @@ msgstr "全体タイムアウト" msgid "Overview" msgstr "概要" +msgid "Passphrase (%s)" +msgstr "暗号フレーズ (%s)" + +msgid "" +"Provides an overview of all configured uplinks for the travelmate interface " +"(%s). You can edit, delete or re-order existing uplinks or scan for a new " +"one. The currently used uplink is emphasized in blue." +msgstr "" +"Travelmate 用インターフェース(%s)に設定済みの全アップリンクの一覧です。既存" +"のアップリンクの編集や削除、並べ替えを行ったり、スキャンを行って新規アップリ" +"ンクを追加することができます。現在使用されているアップリンクは、青色で強調さ" +"れます。" + msgid "Radio selection" msgstr "無線の選択" -msgid "Restrict interface trigger to certain interface(s)" -msgstr "インターフェース トリガーを特定のインターフェースに限定する" +msgid "Repeat scan" +msgstr "再スキャン" + +msgid "Rescan" +msgstr "再スキャン" msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'" msgstr "Travelmate が特定の無線に接続するようにします。例: 'radio0'" @@ -124,26 +194,26 @@ msgstr "Travelmate が特定の無線に接続するようにします。例: 'r msgid "Runtime information" msgstr "実行情報" -msgid "" -"Scan & Add new wireless stations via standard <a href=\"%s\">Wireless " -"Setup</a>" -msgstr "" -"通常の<a href=\"%s\">無線設定</a>にて、新規の無線ステーションのスキャン及び追" -"加を行います。" +msgid "SSID" +msgstr "SSID" -msgid "" -"Space separated list of interfaces that trigger travelmate processing. To " -"disable event driven (re-)starts remove all entries." -msgstr "" -"Travelmate の処理のトリガーとなる、スペースで区切られたインターフェースのリス" -"トです。処理を発生させるイベントを無効にするには、全てのエントリーを削除して" -"空欄にします。" +msgid "SSID (hidden)" +msgstr "SSID(ステルス)" + +msgid "Scan" +msgstr "スキャン:" + +msgid "Signal strength" +msgstr "信号強度" + +msgid "Specify the secret encryption key here." +msgstr "暗号キーを設定します。" msgid "Station Interface" msgstr "ステーション インターフェース" msgid "Station Radio" -msgstr "ステーション 無線" +msgstr "ステーション電波" msgid "Station SSID" msgstr "ステーション SSID" @@ -198,9 +268,39 @@ msgstr "Travelmate バージョン" msgid "Trigger delay" msgstr "トリガー遅延" +msgid "Unknown" +msgstr "不明" + +msgid "Uplink / Trigger interface" +msgstr "アップリンク/トリガー インターフェース" + +msgid "Uplink SSID" +msgstr "アップリンク SSID" + +msgid "Uplink interface" +msgstr "アップリンク インターフェース" + msgid "View Logfile" msgstr "ログファイルの確認" +msgid "WEP" +msgstr "WEP" + +msgid "WEP passphrase" +msgstr "WEP 暗号キー" + +msgid "WPA / WPA2" +msgstr "WPA / WPA2" + +msgid "WPA passphrase" +msgstr "WPA 暗号キー" + +msgid "Wireless Scan" +msgstr "無線スキャン" + +msgid "Wireless Stations" +msgstr "無線ステーション" + msgid "" "add it to the wan zone of the firewall. This step has only to be done once." msgstr "" @@ -210,126 +310,11 @@ msgstr "" msgid "connected" msgstr "接続済み" +msgid "hidden" +msgstr "(不明)" + msgid "n/a" msgstr "利用不可" msgid "not connected" msgstr "未接続" - -#~ msgid "." -#~ msgstr "。" - -#~ msgid "" -#~ "Automatically create a new wireless wan interface, configure it to use " -#~ "dhcp and add it to the wan zone of the firewall. This step has only to be " -#~ "done once." -#~ msgstr "" -#~ "新しい無線 WAN インターフェースを自動的に作成し、DHCP を使用するよう構成し" -#~ "てファイアウォールの wan ゾーンに追加します。このステップは、一度だけ実行" -#~ "する必要があります。" - -#~ msgid "Direct Link: <a href=\"%s\">Wireless Setup</a>" -#~ msgstr "ダイレクト リンク: <a href=\"%s\">無線設定</a>" - -#~ msgid "For further information" -#~ msgstr "詳細情報は" - -#~ msgid "Name of the new wireless wan interface" -#~ msgstr "新しい無線 WAN のインターフェース名" - -#~ msgid "" -#~ "Network Interface '%s' created successfully. Feel free to scan & add new " -#~ "stations via standard wireless setup." -#~ msgstr "" -#~ "ネットワーク インターフェース '%s' の作成に成功しました。通常の無線設定に" -#~ "て、スキャン及び新規ステーションの追加が可能です。" - -#~ msgid "Setup WWAN Interface" -#~ msgstr "WWAN インターフェース設定" - -#~ msgid "" -#~ "The allowed characters are: <code>A-Z</code>, <code>a-z</code>, " -#~ "<code>0-9</code> and <code>_</code> (3-15 characters)." -#~ msgstr "" -#~ "使用可能文字: <code>A-Z</code>, <code>a-z</code>, <code>0-9</code> and " -#~ "<code>_</code>(3 - 15文字)" - -#~ msgid "The given network interface name already exist" -#~ msgstr "入力されたネットワーク インターフェース名は、既に存在しています。" - -#~ msgid "see online documentation" -#~ msgstr "オンライン ドキュメントを確認してください" - -#~ msgid "" -#~ "Brief advice: Create a wwan interface, configure it to use dhcp and add " -#~ "it to the wan zone in firewall. Create the wifi interfaces to be used " -#~ "('client' mode, assigned to wwan network, left as disabled). Travelmate " -#~ "will try to connect to the known wifi client interfaces in the defined " -#~ "order." -#~ msgstr "" -#~ "簡単な解説: 予めWWANインターフェースを作成し、DHCPを使用するよう構成して" -#~ "ファイアウォールのWANゾーンに追加します。また、使用される無線インター" -#~ "フェースを作成しておきます(\"クライアント\" モード、WWANに割り当て、無効" -#~ "状態)。Travelmateは、登録されている順序で既知の無線クライアント インター" -#~ "フェースへの接続を試行します。" - -#~ msgid "" -#~ "Configuration of the Travelmate package to enable travel router " -#~ "functionality." -#~ msgstr "トラベル ルータ機能を有効にする、Travelmate パッケージの設定です。" - -#~ msgid "Debug logging" -#~ msgstr "デバッグ ログ" - -#~ msgid "Default 20, range 10-60" -#~ msgstr "既定値 20、範囲 10 - 60" - -#~ msgid "Default 3, range 1-10" -#~ msgstr "既定値 3、範囲 1 - 10" - -#~ msgid "Disable this if you want to use iwinfo instead of iw" -#~ msgstr "iw の代わりに iwinfo を使用したい場合、この設定を無効にします。" - -#~ msgid "Enable Travelmate" -#~ msgstr "Travelmateの有効化" - -#~ msgid "Global options" -#~ msgstr "全般オプション" - -#~ msgid "Link to detailed advice" -#~ msgstr "詳細な解説へのリンク" - -#~ msgid "Max. number of connection retries to an uplink" -#~ msgstr "確立までの接続試行回数" - -#~ msgid "Max. timeout in seconds for wlan interface reload" -#~ msgstr "無線LANインターフェース リロード時の最大待機時間(秒)" - -#~ msgid "Restrict reload trigger to certain interface(s)" -#~ msgstr "リロード トリガを特定のインターフェースに限定する" - -#~ msgid "" -#~ "Space separated list of wwan interfaces that trigger reload action. To " -#~ "disable reload trigger set it to 'false'. Default: empty" -#~ msgstr "" -#~ "リロード動作のトリガとなる、スペースで区切られたWWAN インターフェースのリ" -#~ "ストです。リロードのトリガを無効にするには、'false' を設定します。既定値:" -#~ "(空)" - -#~ msgid "Use iw for scanning" -#~ msgstr "スキャンに iw を使用する" - -#~ msgid "Default 3, range 0-10. Set to 0 to allow unlimited retries" -#~ msgstr "既定値 3、範囲 0 - 10。再試行回数を制限しない場合、0 に設定します。" - -#~ msgid "Default 30, range 5-60" -#~ msgstr "既定値 30、範囲 5 - 60" - -#~ msgid "Default: empty = use all radios." -#~ msgstr "デフォルト:(空)= 全ての無線を使用" - -#~ msgid "Loop timeout in seconds for wlan monitoring" -#~ msgstr "無線LAN モニターのループ タイムアウト(秒)" - -#~ msgid "Use only one radio, e.g. 'radio0'" -#~ msgstr "単一の無線のみ使用する 例: 'radio0'" diff --git a/applications/luci-app-travelmate/po/pt-br/travelmate.po b/applications/luci-app-travelmate/po/pt-br/travelmate.po index 4eff34e3e1..d74373d8c5 100644 --- a/applications/luci-app-travelmate/po/pt-br/travelmate.po +++ b/applications/luci-app-travelmate/po/pt-br/travelmate.po @@ -12,12 +12,18 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Language: pt_BR\n" -msgid "<br /> Network Interface 'trm_wwan' created successfully." +msgid "Actions" msgstr "" msgid "Add Interface" msgstr "" +msgid "Add Uplink" +msgstr "" + +msgid "Add Wireless Uplink Configuration" +msgstr "" + msgid "" "Additional trigger delay in seconds before travelmate processing begins." msgstr "" @@ -25,9 +31,7 @@ msgstr "" msgid "Advanced" msgstr "" -msgid "" -"Automatically create a new wireless wan uplink interface 'trm_wwan', " -"configure it to use dhcp and" +msgid "Back to overview" msgstr "" msgid "" @@ -41,6 +45,22 @@ msgstr "" msgid "Create Uplink Interface" msgstr "" +msgid "" +"Create a new wireless wan uplink interface, configure it to use dhcp and" +msgstr "" + +msgid "Delete" +msgstr "" + +msgid "Delete this Uplink" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Edit" +msgstr "" + msgid "Edit Firewall Configuration" msgstr "" @@ -53,6 +73,12 @@ msgstr "" msgid "Edit Wireless Configuration" msgstr "" +msgid "Edit Wireless Uplink Configuration" +msgstr "" + +msgid "Edit this Uplink" +msgstr "" + msgid "Enable 'automatic' mode" msgstr "" @@ -62,9 +88,15 @@ msgstr "" msgid "Enable verbose debug logging" msgstr "" +msgid "Encryption" +msgstr "" + msgid "Extra options" msgstr "Opções adicionais" +msgid "Find and join network on" +msgstr "" + msgid "" "For further information <a href=\"%s\" target=\"_blank\">see online " "documentation</a>" @@ -79,21 +111,40 @@ msgstr "" msgid "Input file not found, please check your configuration." msgstr "" -msgid "Interface Setup" +msgid "Interface Timeout" msgstr "" -msgid "Interface Timeout" +msgid "Interface Wizard" msgstr "" -msgid "Keep travelmate in an active state." +msgid "" +"Keep travelmate in an active state. Check every n seconds the connection " +"status, i.e. the uplink availability." msgstr "" msgid "Last rundate" msgstr "" +msgid "Manual Rescan" +msgstr "" + +msgid "Move down" +msgstr "" + +msgid "Move up" +msgstr "" + +msgid "" +"Name of the uplink interface that triggers travelmate processing in 'manual' " +"mode." +msgstr "" + msgid "Online Status" msgstr "" +msgid "Open" +msgstr "" + msgid "" "Options for further tweaking in case the defaults are not suitable for you." msgstr "" @@ -104,10 +155,22 @@ msgstr "" msgid "Overview" msgstr "" +msgid "Passphrase (%s)" +msgstr "" + +msgid "" +"Provides an overview of all configured uplinks for the travelmate interface " +"(%s). You can edit, delete or re-order existing uplinks or scan for a new " +"one. The currently used uplink is emphasized in blue." +msgstr "" + msgid "Radio selection" msgstr "" -msgid "Restrict interface trigger to certain interface(s)" +msgid "Repeat scan" +msgstr "" + +msgid "Rescan" msgstr "" msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'" @@ -116,14 +179,19 @@ msgstr "" msgid "Runtime information" msgstr "" -msgid "" -"Scan & Add new wireless stations via standard <a href=\"%s\">Wireless " -"Setup</a>" +msgid "SSID" msgstr "" -msgid "" -"Space separated list of interfaces that trigger travelmate processing. To " -"disable event driven (re-)starts remove all entries." +msgid "SSID (hidden)" +msgstr "" + +msgid "Scan" +msgstr "" + +msgid "Signal strength" +msgstr "" + +msgid "Specify the secret encryption key here." msgstr "" msgid "Station Interface" @@ -175,9 +243,39 @@ msgstr "" msgid "Trigger delay" msgstr "" +msgid "Unknown" +msgstr "" + +msgid "Uplink / Trigger interface" +msgstr "" + +msgid "Uplink SSID" +msgstr "" + +msgid "Uplink interface" +msgstr "" + msgid "View Logfile" msgstr "" +msgid "WEP" +msgstr "" + +msgid "WEP passphrase" +msgstr "" + +msgid "WPA / WPA2" +msgstr "" + +msgid "WPA passphrase" +msgstr "" + +msgid "Wireless Scan" +msgstr "" + +msgid "Wireless Stations" +msgstr "" + msgid "" "add it to the wan zone of the firewall. This step has only to be done once." msgstr "" @@ -185,6 +283,9 @@ msgstr "" msgid "connected" msgstr "" +msgid "hidden" +msgstr "" + msgid "n/a" msgstr "" diff --git a/applications/luci-app-travelmate/po/templates/travelmate.pot b/applications/luci-app-travelmate/po/templates/travelmate.pot index 615c3a79ce..dc0583fb70 100644 --- a/applications/luci-app-travelmate/po/templates/travelmate.pot +++ b/applications/luci-app-travelmate/po/templates/travelmate.pot @@ -1,12 +1,18 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" -msgid "<br /> Network Interface 'trm_wwan' created successfully." +msgid "Actions" msgstr "" msgid "Add Interface" msgstr "" +msgid "Add Uplink" +msgstr "" + +msgid "Add Wireless Uplink Configuration" +msgstr "" + msgid "" "Additional trigger delay in seconds before travelmate processing begins." msgstr "" @@ -14,9 +20,7 @@ msgstr "" msgid "Advanced" msgstr "" -msgid "" -"Automatically create a new wireless wan uplink interface 'trm_wwan', " -"configure it to use dhcp and" +msgid "Back to overview" msgstr "" msgid "" @@ -30,6 +34,22 @@ msgstr "" msgid "Create Uplink Interface" msgstr "" +msgid "" +"Create a new wireless wan uplink interface, configure it to use dhcp and" +msgstr "" + +msgid "Delete" +msgstr "" + +msgid "Delete this Uplink" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Edit" +msgstr "" + msgid "Edit Firewall Configuration" msgstr "" @@ -42,6 +62,12 @@ msgstr "" msgid "Edit Wireless Configuration" msgstr "" +msgid "Edit Wireless Uplink Configuration" +msgstr "" + +msgid "Edit this Uplink" +msgstr "" + msgid "Enable 'automatic' mode" msgstr "" @@ -51,9 +77,15 @@ msgstr "" msgid "Enable verbose debug logging" msgstr "" +msgid "Encryption" +msgstr "" + msgid "Extra options" msgstr "" +msgid "Find and join network on" +msgstr "" + msgid "" "For further information <a href=\"%s\" target=\"_blank\">see online " "documentation</a>" @@ -68,21 +100,40 @@ msgstr "" msgid "Input file not found, please check your configuration." msgstr "" -msgid "Interface Setup" +msgid "Interface Timeout" msgstr "" -msgid "Interface Timeout" +msgid "Interface Wizard" msgstr "" -msgid "Keep travelmate in an active state." +msgid "" +"Keep travelmate in an active state. Check every n seconds the connection " +"status, i.e. the uplink availability." msgstr "" msgid "Last rundate" msgstr "" +msgid "Manual Rescan" +msgstr "" + +msgid "Move down" +msgstr "" + +msgid "Move up" +msgstr "" + +msgid "" +"Name of the uplink interface that triggers travelmate processing in 'manual' " +"mode." +msgstr "" + msgid "Online Status" msgstr "" +msgid "Open" +msgstr "" + msgid "" "Options for further tweaking in case the defaults are not suitable for you." msgstr "" @@ -93,10 +144,22 @@ msgstr "" msgid "Overview" msgstr "" +msgid "Passphrase (%s)" +msgstr "" + +msgid "" +"Provides an overview of all configured uplinks for the travelmate interface " +"(%s). You can edit, delete or re-order existing uplinks or scan for a new " +"one. The currently used uplink is emphasized in blue." +msgstr "" + msgid "Radio selection" msgstr "" -msgid "Restrict interface trigger to certain interface(s)" +msgid "Repeat scan" +msgstr "" + +msgid "Rescan" msgstr "" msgid "Restrict travelmate to a dedicated radio, e.g. 'radio0'" @@ -105,14 +168,19 @@ msgstr "" msgid "Runtime information" msgstr "" -msgid "" -"Scan & Add new wireless stations via standard <a href=\"%s\">Wireless " -"Setup</a>" +msgid "SSID" msgstr "" -msgid "" -"Space separated list of interfaces that trigger travelmate processing. To " -"disable event driven (re-)starts remove all entries." +msgid "SSID (hidden)" +msgstr "" + +msgid "Scan" +msgstr "" + +msgid "Signal strength" +msgstr "" + +msgid "Specify the secret encryption key here." msgstr "" msgid "Station Interface" @@ -164,9 +232,39 @@ msgstr "" msgid "Trigger delay" msgstr "" +msgid "Unknown" +msgstr "" + +msgid "Uplink / Trigger interface" +msgstr "" + +msgid "Uplink SSID" +msgstr "" + +msgid "Uplink interface" +msgstr "" + msgid "View Logfile" msgstr "" +msgid "WEP" +msgstr "" + +msgid "WEP passphrase" +msgstr "" + +msgid "WPA / WPA2" +msgstr "" + +msgid "WPA passphrase" +msgstr "" + +msgid "Wireless Scan" +msgstr "" + +msgid "Wireless Stations" +msgstr "" + msgid "" "add it to the wan zone of the firewall. This step has only to be done once." msgstr "" @@ -174,6 +272,9 @@ msgstr "" msgid "connected" msgstr "" +msgid "hidden" +msgstr "" + msgid "n/a" msgstr "" diff --git a/applications/luci-app-watchcat/po/sv/watchcat.po b/applications/luci-app-watchcat/po/sv/watchcat.po index 07aa726493..96c73e3111 100644 --- a/applications/luci-app-watchcat/po/sv/watchcat.po +++ b/applications/luci-app-watchcat/po/sv/watchcat.po @@ -19,8 +19,8 @@ msgid "" "How often to check internet connection. Default unit is seconds, you can you " "use the suffix 'm' for minutes, 'h' for hours or 'd' for days" msgstr "" -"Hur ofta internet-anslutningen ska kollas. Standardenheten är sekunder, du kan använda " -"tillägget 'm' för minutrar, 't' för timmar eller 'd' för dagar" +"Hur ofta internet-anslutningen ska kollas. Standardenheten är sekunder, du " +"kan använda tillägget 'm' för minutrar, 't' för timmar eller 'd' för dagar" msgid "" "In periodic mode, it defines the reboot period. In internet mode, it defines " diff --git a/applications/luci-app-wifischedule/po/sv/wifischedule.po b/applications/luci-app-wifischedule/po/sv/wifischedule.po index ca4e5aac78..50953aa285 100644 --- a/applications/luci-app-wifischedule/po/sv/wifischedule.po +++ b/applications/luci-app-wifischedule/po/sv/wifischedule.po @@ -1,5 +1,5 @@ msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" +msgstr "Content-Type: text/plain; charset=UTF-8\n" msgid "Activate wifi" msgstr "Aktivera wifi" diff --git a/applications/luci-app-wireguard/po/sv/wireguard.po b/applications/luci-app-wireguard/po/sv/wireguard.po index b7e3ed5b58..1aa68e251e 100644 --- a/applications/luci-app-wireguard/po/sv/wireguard.po +++ b/applications/luci-app-wireguard/po/sv/wireguard.po @@ -1,5 +1,5 @@ msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8" +msgstr "Content-Type: text/plain; charset=UTF-8\n" msgid "Allowed IPs" msgstr "Tillåtna IP-adresser" |