gluon-web: add i18n package namespaces
This commit is contained in:
parent
1a426c3bb9
commit
557565e189
@ -1,14 +1,16 @@
|
|||||||
return function(form, uci)
|
return function(form, uci)
|
||||||
|
local pkg_i18n = i18n 'gluon-config-mode-autoupdater'
|
||||||
|
|
||||||
local enabled = uci:get_bool("autoupdater", "settings", "enabled")
|
local enabled = uci:get_bool("autoupdater", "settings", "enabled")
|
||||||
if enabled then
|
if enabled then
|
||||||
form:section(
|
form:section(
|
||||||
Section, nil,
|
Section, nil,
|
||||||
translate('This node will automatically update its firmware when a new version is available.')
|
pkg_i18n.translate('This node will automatically update its firmware when a new version is available.')
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
form:section(
|
form:section(
|
||||||
Section, nil,
|
Section, nil,
|
||||||
translate('Automatic updates are disabled. They can be enabled in <em>Advanced settings</em>.')
|
pkg_i18n.translate('Automatic updates are disabled. They can be enabled in <em>Advanced settings</em>.')
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
return function(form, uci)
|
return function(form, uci)
|
||||||
|
local pkg_i18n = i18n 'gluon-config-mode-contact-info'
|
||||||
|
|
||||||
local site = require 'gluon.site'
|
local site = require 'gluon.site'
|
||||||
|
|
||||||
local owner = uci:get_first("gluon-node-info", "owner")
|
local owner = uci:get_first("gluon-node-info", "owner")
|
||||||
|
|
||||||
local s = form:section(Section, nil, translate(
|
local s = form:section(Section, nil, pkg_i18n.translate(
|
||||||
'Please provide your contact information here to '
|
'Please provide your contact information here to '
|
||||||
.. 'allow others to contact you. Note that '
|
.. 'allow others to contact you. Note that '
|
||||||
.. 'this information will be visible <em>publicly</em> '
|
.. 'this information will be visible <em>publicly</em> '
|
||||||
.. 'on the internet together with your node\'s coordinates.'
|
.. 'on the internet together with your node\'s coordinates.'
|
||||||
))
|
))
|
||||||
|
|
||||||
local o = s:option(Value, "contact", translate("Contact info"), translate("e.g. E-mail or phone number"))
|
local o = s:option(Value, "contact", pkg_i18n.translate("Contact info"), pkg_i18n.translate("e.g. E-mail or phone number"))
|
||||||
o.default = uci:get("gluon-node-info", owner, "contact")
|
o.default = uci:get("gluon-node-info", owner, "contact")
|
||||||
o.optional = not site.config_mode.owner.obligatory(false)
|
o.optional = not site.config_mode.owner.obligatory(false)
|
||||||
-- without a minimal length, an empty string will be accepted even with "optional = false"
|
-- without a minimal length, an empty string will be accepted even with "optional = false"
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<%-
|
<%-
|
||||||
|
local site_i18n = i18n 'gluon-site'
|
||||||
|
|
||||||
local sysconfig = require 'gluon.sysconfig'
|
local sysconfig = require 'gluon.sysconfig'
|
||||||
|
|
||||||
local msg = _translate('gluon-config-mode:welcome')
|
local msg = site_i18n._translate('gluon-config-mode:welcome')
|
||||||
if not msg then return end
|
if not msg then return end
|
||||||
-%>
|
-%>
|
||||||
<p>
|
<p>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
local site_i18n = i18n 'gluon-site'
|
||||||
|
|
||||||
local site = require 'gluon.site'
|
local site = require 'gluon.site'
|
||||||
local sysconfig = require 'gluon.sysconfig'
|
local sysconfig = require 'gluon.sysconfig'
|
||||||
local pretty_hostname = require 'pretty_hostname'
|
local pretty_hostname = require 'pretty_hostname'
|
||||||
@ -7,7 +9,7 @@ local uci = require("simple-uci").cursor()
|
|||||||
local hostname = pretty_hostname.get(uci)
|
local hostname = pretty_hostname.get(uci)
|
||||||
local contact = uci:get_first('gluon-node-info', 'owner', 'contact')
|
local contact = uci:get_first('gluon-node-info', 'owner', 'contact')
|
||||||
|
|
||||||
local msg = _translate('gluon-config-mode:reboot')
|
local msg = site_i18n._translate('gluon-config-mode:reboot')
|
||||||
if not msg then return end
|
if not msg then return end
|
||||||
|
|
||||||
renderer.render_string(msg, {
|
renderer.render_string(msg, {
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
|
package 'gluon-config-mode-core'
|
||||||
|
|
||||||
entry({}, alias("wizard"))
|
entry({}, alias("wizard"))
|
||||||
entry({"wizard"}, model("gluon-config-mode/wizard"), _("Wizard"), 5)
|
entry({"wizard"}, model("gluon-config-mode/wizard"), _("Wizard"), 5)
|
||||||
|
@ -26,6 +26,7 @@ f.reset = false
|
|||||||
|
|
||||||
local s = f:section(Section)
|
local s = f:section(Section)
|
||||||
s.template = "gluon/config-mode/welcome"
|
s.template = "gluon/config-mode/welcome"
|
||||||
|
s.package = "gluon-config-mode-core"
|
||||||
|
|
||||||
local commit = {'gluon-setup-mode'}
|
local commit = {'gluon-setup-mode'}
|
||||||
local run = {}
|
local run = {}
|
||||||
@ -57,6 +58,7 @@ function f:write()
|
|||||||
end
|
end
|
||||||
|
|
||||||
f.template = "gluon/config-mode/reboot"
|
f.template = "gluon/config-mode/reboot"
|
||||||
|
f.package = "gluon-config-mode-core"
|
||||||
f.hidenav = true
|
f.hidenav = true
|
||||||
|
|
||||||
if nixio.fork() == 0 then
|
if nixio.fork() == 0 then
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
return function(form, uci)
|
return function(form, uci)
|
||||||
|
local site_i18n = i18n 'gluon-site'
|
||||||
|
|
||||||
local fs = require 'nixio.fs'
|
local fs = require 'nixio.fs'
|
||||||
local json = require 'jsonc'
|
local json = require 'jsonc'
|
||||||
local site = require 'gluon.site'
|
local site = require 'gluon.site'
|
||||||
@ -24,8 +26,8 @@ return function(form, uci)
|
|||||||
return list
|
return list
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = form:section(Section, nil, translate('gluon-config-mode:domain-select'))
|
local s = form:section(Section, nil, site_i18n.translate('gluon-config-mode:domain-select'))
|
||||||
local o = s:option(ListValue, 'domain', translate('gluon-config-mode:domain'))
|
local o = s:option(ListValue, 'domain', site_i18n.translate('gluon-config-mode:domain'))
|
||||||
|
|
||||||
if configured then
|
if configured then
|
||||||
o.default = selected_domain
|
o.default = selected_domain
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
return function(form, uci)
|
return function(form, uci)
|
||||||
|
local pkg_i18n = i18n 'gluon-config-mode-geo-location'
|
||||||
|
local site_i18n = i18n 'gluon-site'
|
||||||
|
|
||||||
local site = require 'gluon.site'
|
local site = require 'gluon.site'
|
||||||
|
|
||||||
local location = uci:get_first("gluon-node-info", "location")
|
local location = uci:get_first("gluon-node-info", "location")
|
||||||
@ -11,25 +14,25 @@ return function(form, uci)
|
|||||||
return uci:get_bool("gluon-node-info", location, "altitude")
|
return uci:get_bool("gluon-node-info", location, "altitude")
|
||||||
end
|
end
|
||||||
|
|
||||||
local text = translate(
|
local text = pkg_i18n.translate(
|
||||||
'If you want the location of your node to ' ..
|
'If you want the location of your node to ' ..
|
||||||
'be displayed on the map, you can enter its coordinates here.'
|
'be displayed on the map, you can enter its coordinates here.'
|
||||||
)
|
)
|
||||||
if show_altitude() then
|
if show_altitude() then
|
||||||
text = text .. ' ' .. translate("gluon-config-mode:altitude-help")
|
text = text .. ' ' .. site_i18n.translate("gluon-config-mode:altitude-help")
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = form:section(Section, nil, text)
|
local s = form:section(Section, nil, text)
|
||||||
|
|
||||||
local o
|
local o
|
||||||
|
|
||||||
local share_location = s:option(Flag, "location", translate("Show node on the map"))
|
local share_location = s:option(Flag, "location", pkg_i18n.translate("Show node on the map"))
|
||||||
share_location.default = uci:get_bool("gluon-node-info", location, "share_location")
|
share_location.default = uci:get_bool("gluon-node-info", location, "share_location")
|
||||||
function share_location:write(data)
|
function share_location:write(data)
|
||||||
uci:set("gluon-node-info", location, "share_location", data)
|
uci:set("gluon-node-info", location, "share_location", data)
|
||||||
end
|
end
|
||||||
|
|
||||||
o = s:option(Value, "latitude", translate("Latitude"), translatef("e.g. %s", "53.873621"))
|
o = s:option(Value, "latitude", pkg_i18n.translate("Latitude"), pkg_i18n.translatef("e.g. %s", "53.873621"))
|
||||||
o.default = uci:get("gluon-node-info", location, "latitude")
|
o.default = uci:get("gluon-node-info", location, "latitude")
|
||||||
o:depends(share_location, true)
|
o:depends(share_location, true)
|
||||||
o.datatype = "float"
|
o.datatype = "float"
|
||||||
@ -37,7 +40,7 @@ return function(form, uci)
|
|||||||
uci:set("gluon-node-info", location, "latitude", data)
|
uci:set("gluon-node-info", location, "latitude", data)
|
||||||
end
|
end
|
||||||
|
|
||||||
o = s:option(Value, "longitude", translate("Longitude"), translatef("e.g. %s", "10.689901"))
|
o = s:option(Value, "longitude", pkg_i18n.translate("Longitude"), pkg_i18n.translatef("e.g. %s", "10.689901"))
|
||||||
o.default = uci:get("gluon-node-info", location, "longitude")
|
o.default = uci:get("gluon-node-info", location, "longitude")
|
||||||
o:depends(share_location, true)
|
o:depends(share_location, true)
|
||||||
o.datatype = "float"
|
o.datatype = "float"
|
||||||
@ -46,7 +49,7 @@ return function(form, uci)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if show_altitude() then
|
if show_altitude() then
|
||||||
o = s:option(Value, "altitude", translate("gluon-config-mode:altitude-label"), translatef("e.g. %s", "11.51"))
|
o = s:option(Value, "altitude", site_i18n.translate("gluon-config-mode:altitude-label"), pkg_i18n.translatef("e.g. %s", "11.51"))
|
||||||
o.default = uci:get("gluon-node-info", location, "altitude")
|
o.default = uci:get("gluon-node-info", location, "altitude")
|
||||||
o:depends(share_location, true)
|
o:depends(share_location, true)
|
||||||
o.datatype = "float"
|
o.datatype = "float"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
return function(form, uci)
|
return function(form, uci)
|
||||||
|
local pkg_i18n = i18n 'gluon-config-mode-hostname'
|
||||||
|
|
||||||
local pretty_hostname = require "pretty_hostname"
|
local pretty_hostname = require "pretty_hostname"
|
||||||
|
|
||||||
local s = form:section(Section)
|
local s = form:section(Section)
|
||||||
local o = s:option(Value, "hostname", translate("Node name"))
|
local o = s:option(Value, "hostname", pkg_i18n.translate("Node name"))
|
||||||
o.default = pretty_hostname.get(uci)
|
o.default = pretty_hostname.get(uci)
|
||||||
|
|
||||||
function o:write(data)
|
function o:write(data)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
local site_i18n = i18n 'gluon-site'
|
||||||
|
|
||||||
local uci = require("simple-uci").cursor()
|
local uci = require("simple-uci").cursor()
|
||||||
local lutil = require "gluon.web.util"
|
local lutil = require "gluon.web.util"
|
||||||
local fs = require "nixio.fs"
|
local fs = require "nixio.fs"
|
||||||
@ -23,15 +25,15 @@ local msg
|
|||||||
if has_tunneldigger then
|
if has_tunneldigger then
|
||||||
local tunneldigger_enabled = uci:get_bool("tunneldigger", "mesh_vpn", "enabled")
|
local tunneldigger_enabled = uci:get_bool("tunneldigger", "mesh_vpn", "enabled")
|
||||||
if not tunneldigger_enabled then
|
if not tunneldigger_enabled then
|
||||||
msg = _translate('gluon-config-mode:novpn')
|
msg = site_i18n._translate('gluon-config-mode:novpn')
|
||||||
end
|
end
|
||||||
elseif has_fastd then
|
elseif has_fastd then
|
||||||
local fastd_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled")
|
local fastd_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled")
|
||||||
if fastd_enabled then
|
if fastd_enabled then
|
||||||
pubkey = util.trim(lutil.exec("/etc/init.d/fastd show_key mesh_vpn"))
|
pubkey = util.trim(lutil.exec("/etc/init.d/fastd show_key mesh_vpn"))
|
||||||
msg = _translate('gluon-config-mode:pubkey')
|
msg = site_i18n._translate('gluon-config-mode:pubkey')
|
||||||
else
|
else
|
||||||
msg = _translate('gluon-config-mode:novpn')
|
msg = site_i18n._translate('gluon-config-mode:novpn')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ return function(form, uci)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local msg = translate(
|
local pkg_i18n = i18n 'gluon-config-mode-mesh-vpn'
|
||||||
|
|
||||||
|
local msg = pkg_i18n.translate(
|
||||||
'Your internet connection can be used to establish a ' ..
|
'Your internet connection can be used to establish a ' ..
|
||||||
'VPN connection with other nodes. ' ..
|
'VPN connection with other nodes. ' ..
|
||||||
'Enable this option if there are no other nodes reachable ' ..
|
'Enable this option if there are no other nodes reachable ' ..
|
||||||
@ -21,7 +23,7 @@ return function(form, uci)
|
|||||||
|
|
||||||
local o
|
local o
|
||||||
|
|
||||||
local meshvpn = s:option(Flag, "meshvpn", translate("Use internet connection (mesh VPN)"))
|
local meshvpn = s:option(Flag, "meshvpn", pkg_i18n.translate("Use internet connection (mesh VPN)"))
|
||||||
meshvpn.default = uci:get_bool("fastd", "mesh_vpn", "enabled") or uci:get_bool("tunneldigger", "mesh_vpn", "enabled")
|
meshvpn.default = uci:get_bool("fastd", "mesh_vpn", "enabled") or uci:get_bool("tunneldigger", "mesh_vpn", "enabled")
|
||||||
function meshvpn:write(data)
|
function meshvpn:write(data)
|
||||||
if has_fastd then
|
if has_fastd then
|
||||||
@ -32,7 +34,7 @@ return function(form, uci)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local limit = s:option(Flag, "limit_enabled", translate("Limit bandwidth"))
|
local limit = s:option(Flag, "limit_enabled", pkg_i18n.translate("Limit bandwidth"))
|
||||||
limit:depends(meshvpn, true)
|
limit:depends(meshvpn, true)
|
||||||
limit.default = uci:get_bool("simple-tc", "mesh_vpn", "enabled")
|
limit.default = uci:get_bool("simple-tc", "mesh_vpn", "enabled")
|
||||||
function limit:write(data)
|
function limit:write(data)
|
||||||
@ -41,7 +43,7 @@ return function(form, uci)
|
|||||||
uci:set("simple-tc", "mesh_vpn", "ifname", "mesh-vpn")
|
uci:set("simple-tc", "mesh_vpn", "ifname", "mesh-vpn")
|
||||||
end
|
end
|
||||||
|
|
||||||
o = s:option(Value, "limit_ingress", translate("Downstream (kbit/s)"))
|
o = s:option(Value, "limit_ingress", pkg_i18n.translate("Downstream (kbit/s)"))
|
||||||
o:depends(limit, true)
|
o:depends(limit, true)
|
||||||
o.default = uci:get("simple-tc", "mesh_vpn", "limit_ingress")
|
o.default = uci:get("simple-tc", "mesh_vpn", "limit_ingress")
|
||||||
o.datatype = "uinteger"
|
o.datatype = "uinteger"
|
||||||
@ -49,7 +51,7 @@ return function(form, uci)
|
|||||||
uci:set("simple-tc", "mesh_vpn", "limit_ingress", data)
|
uci:set("simple-tc", "mesh_vpn", "limit_ingress", data)
|
||||||
end
|
end
|
||||||
|
|
||||||
o = s:option(Value, "limit_egress", translate("Upstream (kbit/s)"))
|
o = s:option(Value, "limit_egress", pkg_i18n.translate("Upstream (kbit/s)"))
|
||||||
o:depends(limit, true)
|
o:depends(limit, true)
|
||||||
o.default = uci:get("simple-tc", "mesh_vpn", "limit_egress")
|
o.default = uci:get("simple-tc", "mesh_vpn", "limit_egress")
|
||||||
o.datatype = "uinteger"
|
o.datatype = "uinteger"
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
package 'gluon-web-admin'
|
||||||
|
|
||||||
|
|
||||||
local root = node()
|
local root = node()
|
||||||
if not root.target then
|
if not root.target then
|
||||||
root.target = alias("admin")
|
root.target = alias("admin")
|
||||||
|
@ -9,6 +9,9 @@ You may obtain a copy of the License at
|
|||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
|
package 'gluon-web-admin'
|
||||||
|
|
||||||
|
|
||||||
local fs = require 'nixio.fs'
|
local fs = require 'nixio.fs'
|
||||||
|
|
||||||
local tmpfile = "/tmp/firmware.img"
|
local tmpfile = "/tmp/firmware.img"
|
||||||
@ -106,17 +109,23 @@ local function action_upgrade(http, renderer)
|
|||||||
|
|
||||||
renderer.render("layout", {
|
renderer.render("layout", {
|
||||||
content = "admin/upgrade",
|
content = "admin/upgrade",
|
||||||
|
env = {
|
||||||
bad_image = has_image and not has_support,
|
bad_image = has_image and not has_support,
|
||||||
|
},
|
||||||
|
pkg = 'gluon-web-admin',
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Step 2: present uploaded file, show checksum, confirmation
|
-- Step 2: present uploaded file, show checksum, confirmation
|
||||||
elseif step == 2 then
|
elseif step == 2 then
|
||||||
renderer.render("layout", {
|
renderer.render("layout", {
|
||||||
content = "admin/upgrade_confirm",
|
content = "admin/upgrade_confirm",
|
||||||
|
env = {
|
||||||
checksum = image_checksum(tmpfile),
|
checksum = image_checksum(tmpfile),
|
||||||
filesize = fs.stat(tmpfile).size,
|
filesize = fs.stat(tmpfile).size,
|
||||||
flashsize = storage_size(),
|
flashsize = storage_size(),
|
||||||
keepconfig = (http:formvalue("keepcfg") == "1"),
|
keepconfig = (http:formvalue("keepcfg") == "1"),
|
||||||
|
},
|
||||||
|
pkg = 'gluon-web-admin',
|
||||||
})
|
})
|
||||||
elseif step == 3 then
|
elseif step == 3 then
|
||||||
if http:formvalue("keepcfg") == "1" then
|
if http:formvalue("keepcfg") == "1" then
|
||||||
@ -127,6 +136,7 @@ local function action_upgrade(http, renderer)
|
|||||||
renderer.render("layout", {
|
renderer.render("layout", {
|
||||||
content = "admin/upgrade_reboot",
|
content = "admin/upgrade_reboot",
|
||||||
hidenav = true,
|
hidenav = true,
|
||||||
|
pkg = 'gluon-web-admin',
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
package 'gluon-web-autoupdater'
|
||||||
|
|
||||||
entry({"admin", "autoupdater"}, model("admin/autoupdater"), _("Automatic updates"), 80)
|
entry({"admin", "autoupdater"}, model("admin/autoupdater"), _("Automatic updates"), 80)
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
package 'gluon-web-logging'
|
||||||
|
|
||||||
entry({"admin", "logging"}, model("admin/logging"), _("Logging"), 85)
|
entry({"admin", "logging"}, model("admin/logging"), _("Logging"), 85)
|
||||||
|
@ -26,14 +26,14 @@ define Build/Configure
|
|||||||
endef
|
endef
|
||||||
|
|
||||||
define Build/Compile
|
define Build/Compile
|
||||||
$(call GluonBuildI18N,gluon-mesh-vpn-fastd,i18n)
|
$(call GluonBuildI18N,gluon-web-mesh-vpn-fastd,i18n)
|
||||||
$(call GluonSrcDiet,./luasrc,$(PKG_BUILD_DIR)/luadest/)
|
$(call GluonSrcDiet,./luasrc,$(PKG_BUILD_DIR)/luadest/)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/gluon-web-mesh-vpn-fastd/install
|
define Package/gluon-web-mesh-vpn-fastd/install
|
||||||
$(CP) ./files/* $(1)/
|
$(CP) ./files/* $(1)/
|
||||||
$(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/
|
$(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/
|
||||||
$(call GluonInstallI18N,gluon-mesh-vpn-fastd,$(1))
|
$(call GluonInstallI18N,gluon-web-mesh-vpn-fastd,$(1))
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/gluon-web-mesh-vpn-fastd/postinst
|
define Package/gluon-web-mesh-vpn-fastd/postinst
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
package 'gluon-web-mesh-vpn-fastd'
|
||||||
|
|
||||||
entry({"admin", "mesh_vpn_fastd"}, model("admin/mesh_vpn_fastd"), _("Mesh VPN"), 50)
|
entry({"admin", "mesh_vpn_fastd"}, model("admin/mesh_vpn_fastd"), _("Mesh VPN"), 50)
|
||||||
|
@ -6,6 +6,7 @@ local f = Form(translate('Mesh VPN'))
|
|||||||
local s = f:section(Section)
|
local s = f:section(Section)
|
||||||
|
|
||||||
local mode = s:option(Value, 'mode')
|
local mode = s:option(Value, 'mode')
|
||||||
|
mode.package = "gluon-web-mesh-vpn-fastd"
|
||||||
mode.template = "gluon/model/mesh-vpn-fastd"
|
mode.template = "gluon/model/mesh-vpn-fastd"
|
||||||
|
|
||||||
local methods = uci:get('fastd', 'mesh_vpn', 'method')
|
local methods = uci:get('fastd', 'mesh_vpn', 'method')
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
package 'gluon-web-network'
|
||||||
|
|
||||||
entry({"admin", "network"}, model("admin/network"), _("Network"), 40)
|
entry({"admin", "network"}, model("admin/network"), _("Network"), 40)
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
package 'gluon-web-node-role'
|
||||||
|
|
||||||
entry({"admin", "noderole"}, model("admin/noderole"), "Node role", 60)
|
entry({"admin", "noderole"}, model("admin/noderole"), "Node role", 60)
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
package 'gluon-web-private-wifi'
|
||||||
|
|
||||||
entry({"admin", "privatewifi"}, model("admin/privatewifi"), _("Private WLAN"), 30)
|
entry({"admin", "privatewifi"}, model("admin/privatewifi"), _("Private WLAN"), 30)
|
||||||
|
@ -33,6 +33,10 @@ You may obtain a copy of the License at
|
|||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function title(node)
|
||||||
|
return i18n(node.pkg).translate(node.title)
|
||||||
|
end
|
||||||
|
|
||||||
local function subtree(prefix, node, name, ...)
|
local function subtree(prefix, node, name, ...)
|
||||||
if not node then return end
|
if not node then return end
|
||||||
|
|
||||||
@ -48,7 +52,7 @@ You may obtain a copy of the License at
|
|||||||
local active = (v == name)
|
local active = (v == name)
|
||||||
%>
|
%>
|
||||||
<li class="tabmenu-item-<%=v%><% if active then %> active<% end %>">
|
<li class="tabmenu-item-<%=v%><% if active then %> active<% end %>">
|
||||||
<a href="<%=url(append(prefix, v))%>"><%=pcdata(translate(child.title))%></a>
|
<a href="<%=url(append(prefix, v))%>"><%=pcdata(title(child))%></a>
|
||||||
</li>
|
</li>
|
||||||
<%
|
<%
|
||||||
end
|
end
|
||||||
@ -71,7 +75,7 @@ You may obtain a copy of the License at
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
|
<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
|
||||||
<title><%=pcdata( hostname .. ( (rnode and rnode.title) and ' - ' .. translate(rnode.title) or '')) %></title>
|
<title><%=pcdata( hostname .. ((rnode and rnode.title) and ' - ' .. title(rnode) or '')) %></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@ -88,7 +92,7 @@ You may obtain a copy of the License at
|
|||||||
<% if #categories > 1 and not hidenav then %>
|
<% if #categories > 1 and not hidenav then %>
|
||||||
<ul id="topmenu">
|
<ul id="topmenu">
|
||||||
<% for i, r in ipairs(categories) do %>
|
<% for i, r in ipairs(categories) do %>
|
||||||
<li><a class="topcat<% if request[1] == r then %> active<%end%>" href="<%=url({r})%>"><%=pcdata(translate(root.nodes[r].title))%></a></li>
|
<li><a class="topcat<% if request[1] == r then %> active<%end%>" href="<%=url({r})%>"><%=pcdata(title(root.nodes[r]))%></a></li>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -110,9 +114,9 @@ You may obtain a copy of the License at
|
|||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<%
|
<%
|
||||||
ok, err = pcall(include, content)
|
ok, err = pcall(renderer.render, content, env, pkg)
|
||||||
if not ok then
|
if not ok then
|
||||||
renderer.render('error500', {message = err})
|
renderer.render('error500', {message = err}, 'gluon-web')
|
||||||
end
|
end
|
||||||
%>
|
%>
|
||||||
|
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
package 'gluon-web-wifi-config'
|
||||||
|
|
||||||
entry({"admin", "wifi-config"}, model("admin/wifi-config"), _("WLAN"), 20)
|
entry({"admin", "wifi-config"}, model("admin/wifi-config"), _("WLAN"), 20)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
|
-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
|
||||||
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
|
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
|
||||||
-- Licensed to the public under the Apache License 2.0.
|
-- Licensed to the public under the Apache License 2.0.
|
||||||
|
|
||||||
local fs = require "nixio.fs"
|
local fs = require "nixio.fs"
|
||||||
@ -77,7 +77,7 @@ local function set_language(renderer, accept)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for match in accept:gmatch("[^,]+") do
|
for match in accept:gmatch("[^,]+") do
|
||||||
local lang = match:match('^%s*([^%s;-_]+)')
|
local lang = match:match('^%s*([^%s;_-]+)')
|
||||||
local q = tonumber(match:match(';q=(%S+)%s*$') or 1)
|
local q = tonumber(match:match(';q=(%S+)%s*$') or 1)
|
||||||
|
|
||||||
if lang == '*' then
|
if lang == '*' then
|
||||||
@ -93,11 +93,7 @@ local function set_language(renderer, accept)
|
|||||||
return (weights[a] or 0) > (weights[b] or 0)
|
return (weights[a] or 0) > (weights[b] or 0)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for _, lang in ipairs(langs) do
|
renderer.set_language(langs)
|
||||||
if renderer.setlanguage(lang) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -147,7 +143,20 @@ function dispatch(http, request)
|
|||||||
url = function(path) return build_url(http, path) end,
|
url = function(path) return build_url(http, path) end,
|
||||||
}, { __index = _G }))
|
}, { __index = _G }))
|
||||||
|
|
||||||
|
|
||||||
|
local function createtree()
|
||||||
|
local base = util.libpath() .. "/controller/"
|
||||||
|
|
||||||
|
local function load_ctl(path)
|
||||||
|
local ctl = assert(loadfile(path))
|
||||||
|
|
||||||
|
local _pkg
|
||||||
|
|
||||||
local subdisp = setmetatable({
|
local subdisp = setmetatable({
|
||||||
|
package = function(name)
|
||||||
|
_pkg = name
|
||||||
|
end,
|
||||||
|
|
||||||
node = function(...)
|
node = function(...)
|
||||||
return _node({...})
|
return _node({...})
|
||||||
end,
|
end,
|
||||||
@ -158,6 +167,7 @@ function dispatch(http, request)
|
|||||||
c.target = target
|
c.target = target
|
||||||
c.title = title
|
c.title = title
|
||||||
c.order = order
|
c.order = order
|
||||||
|
c.pkg = _pkg
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end,
|
end,
|
||||||
@ -177,17 +187,19 @@ function dispatch(http, request)
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
template = function(view)
|
template = function(view)
|
||||||
|
local pkg = _pkg
|
||||||
return function()
|
return function()
|
||||||
renderer.render("layout", {content = view})
|
renderer.render("layout", {content = view, pkg = pkg})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
model = function(name)
|
model = function(name)
|
||||||
|
local pkg = _pkg
|
||||||
return function()
|
return function()
|
||||||
local hidenav = false
|
local hidenav = false
|
||||||
|
|
||||||
local model = require "gluon.web.model"
|
local model = require "gluon.web.model"
|
||||||
local maps = model.load(name, renderer)
|
local maps = model.load(name, renderer, pkg)
|
||||||
|
|
||||||
for _, map in ipairs(maps) do
|
for _, map in ipairs(maps) do
|
||||||
map:parse(http)
|
map:parse(http)
|
||||||
@ -199,7 +211,9 @@ function dispatch(http, request)
|
|||||||
|
|
||||||
renderer.render("layout", {
|
renderer.render("layout", {
|
||||||
content = "model/wrapper",
|
content = "model/wrapper",
|
||||||
|
env = {
|
||||||
maps = maps,
|
maps = maps,
|
||||||
|
},
|
||||||
hidenav = hidenav,
|
hidenav = hidenav,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
@ -210,12 +224,6 @@ function dispatch(http, request)
|
|||||||
end,
|
end,
|
||||||
}, { __index = _G })
|
}, { __index = _G })
|
||||||
|
|
||||||
local function createtree()
|
|
||||||
local base = util.libpath() .. "/controller/"
|
|
||||||
|
|
||||||
local function load_ctl(path)
|
|
||||||
local ctl = assert(loadfile(path))
|
|
||||||
|
|
||||||
local env = setmetatable({}, { __index = subdisp })
|
local env = setmetatable({}, { __index = subdisp })
|
||||||
setfenv(ctl, env)
|
setfenv(ctl, env)
|
||||||
|
|
||||||
@ -239,9 +247,14 @@ function dispatch(http, request)
|
|||||||
|
|
||||||
if not node or not node.target then
|
if not node or not node.target then
|
||||||
http:status(404, "Not Found")
|
http:status(404, "Not Found")
|
||||||
renderer.render("layout", { content = "error404", message =
|
renderer.render("layout", {
|
||||||
|
content = "error404",
|
||||||
|
env = {
|
||||||
|
message =
|
||||||
"No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
|
"No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
|
||||||
"If this URL belongs to an extension, make sure it is properly installed.\n"
|
"If this URL belongs to an extension, make sure it is properly installed.\n",
|
||||||
|
},
|
||||||
|
pkg = 'gluon-web',
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -251,9 +264,14 @@ function dispatch(http, request)
|
|||||||
local ok, err = pcall(node.target)
|
local ok, err = pcall(node.target)
|
||||||
if not ok then
|
if not ok then
|
||||||
http:status(500, "Internal Server Error")
|
http:status(500, "Internal Server Error")
|
||||||
renderer.render("layout", { content = "error500", message =
|
renderer.render("layout", {
|
||||||
|
content = "error500",
|
||||||
|
env = {
|
||||||
|
message =
|
||||||
"Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
|
"Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
|
||||||
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)")
|
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"),
|
||||||
|
},
|
||||||
|
pkg = 'gluon-web',
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
54
package/gluon-web/luasrc/usr/lib/lua/gluon/web/i18n.lua
Normal file
54
package/gluon-web/luasrc/usr/lib/lua/gluon/web/i18n.lua
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
-- Copyright 2018 Matthias Schiffer <mschiffer@universe-factory.net>
|
||||||
|
-- Licensed to the public under the Apache License 2.0.
|
||||||
|
|
||||||
|
local tparser = require "gluon.web.template.parser"
|
||||||
|
local util = require "gluon.web.util"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
|
||||||
|
|
||||||
|
local i18ndir = util.libpath() .. "/i18n"
|
||||||
|
|
||||||
|
|
||||||
|
local function i18n_file(lang, pkg)
|
||||||
|
return string.format('%s/%s.%s.lmo', i18ndir, pkg, lang)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function no_translation(key)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function load_catalog(lang, pkg)
|
||||||
|
if pkg then
|
||||||
|
local file = i18n_file(lang, pkg)
|
||||||
|
local cat = fs.access(file) and tparser.load_catalog(file)
|
||||||
|
|
||||||
|
if cat then return cat end
|
||||||
|
end
|
||||||
|
|
||||||
|
return no_translation
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
module "gluon.web.i18n"
|
||||||
|
|
||||||
|
function supported(lang)
|
||||||
|
return lang == 'en' or fs.access(i18n_file(lang, 'gluon-web'))
|
||||||
|
end
|
||||||
|
|
||||||
|
function load(lang, pkg)
|
||||||
|
local _translate = load_catalog(lang, pkg)
|
||||||
|
|
||||||
|
local function translate(key)
|
||||||
|
return _translate(key) or key
|
||||||
|
end
|
||||||
|
|
||||||
|
local function translatef(key, ...)
|
||||||
|
return translate(key):format(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
_translate = _translate,
|
||||||
|
translate = translate,
|
||||||
|
translatef = translatef,
|
||||||
|
}
|
||||||
|
end
|
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
module("gluon.web.model", package.seeall)
|
module("gluon.web.model", package.seeall)
|
||||||
|
|
||||||
local util = require("gluon.web.util")
|
local util = require "gluon.web.util"
|
||||||
|
|
||||||
local fs = require("nixio.fs")
|
local fs = require "nixio.fs"
|
||||||
local datatypes = require("gluon.web.model.datatypes")
|
local datatypes = require "gluon.web.model.datatypes"
|
||||||
local dispatcher = require("gluon.web.dispatcher")
|
local dispatcher = require "gluon.web.dispatcher"
|
||||||
local class = util.class
|
local class = util.class
|
||||||
local instanceof = util.instanceof
|
local instanceof = util.instanceof
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ FORM_VALID = 1
|
|||||||
FORM_INVALID = -1
|
FORM_INVALID = -1
|
||||||
|
|
||||||
-- Loads a model from given file, creating an environment and returns it
|
-- Loads a model from given file, creating an environment and returns it
|
||||||
function load(name, renderer)
|
function load(name, renderer, pkg)
|
||||||
local modeldir = util.libpath() .. "/model/"
|
local modeldir = util.libpath() .. "/model/"
|
||||||
|
|
||||||
if not fs.access(modeldir..name..".lua") then
|
if not fs.access(modeldir..name..".lua") then
|
||||||
@ -26,14 +26,16 @@ function load(name, renderer)
|
|||||||
|
|
||||||
local func = assert(loadfile(modeldir..name..".lua"))
|
local func = assert(loadfile(modeldir..name..".lua"))
|
||||||
|
|
||||||
local env = {
|
local i18n = setmetatable({
|
||||||
translate=renderer.translate,
|
i18n = renderer.i18n
|
||||||
translatef=renderer.translatef,
|
}, {
|
||||||
}
|
__index = renderer.i18n(pkg)
|
||||||
|
})
|
||||||
|
|
||||||
setfenv(func, setmetatable(env, {__index =
|
|
||||||
|
setfenv(func, setmetatable({}, {__index =
|
||||||
function(tbl, key)
|
function(tbl, key)
|
||||||
return _M[key] or _G[key]
|
return _M[key] or i18n[key] or _G[key]
|
||||||
end
|
end
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -85,6 +87,7 @@ function Node:__init__(title, description, name)
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.index = nil
|
self.index = nil
|
||||||
self.parent = nil
|
self.parent = nil
|
||||||
|
self.package = 'gluon-web'
|
||||||
end
|
end
|
||||||
|
|
||||||
function Node:append(obj)
|
function Node:append(obj)
|
||||||
@ -116,7 +119,7 @@ function Node:render(renderer, scope)
|
|||||||
id = self:id(),
|
id = self:id(),
|
||||||
scope = scope,
|
scope = scope,
|
||||||
}, {__index = scope})
|
}, {__index = scope})
|
||||||
renderer.render(self.template, env)
|
renderer.render(self.template, env, self.package)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,39 +1,59 @@
|
|||||||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
|
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
|
||||||
-- Licensed to the public under the Apache License 2.0.
|
-- Licensed to the public under the Apache License 2.0.
|
||||||
|
|
||||||
local tparser = require "gluon.web.template.parser"
|
local tparser = require "gluon.web.template.parser"
|
||||||
|
local i18n = require "gluon.web.i18n"
|
||||||
local util = require "gluon.web.util"
|
local util = require "gluon.web.util"
|
||||||
local fs = require "nixio.fs"
|
|
||||||
|
|
||||||
local tostring, setmetatable, setfenv, pcall, assert = tostring, setmetatable, setfenv, pcall, assert
|
local tostring, ipairs, setmetatable, setfenv = tostring, ipairs, setmetatable, setfenv
|
||||||
|
local pcall, assert = pcall, assert
|
||||||
|
|
||||||
|
|
||||||
module "gluon.web.template"
|
module "gluon.web.template"
|
||||||
|
|
||||||
local viewdir = util.libpath() .. "/view/"
|
local viewdir = util.libpath() .. "/view/"
|
||||||
local i18ndir = util.libpath() .. "/i18n/"
|
|
||||||
|
|
||||||
function renderer(env)
|
function renderer(env)
|
||||||
local ctx = {}
|
local ctx = {}
|
||||||
|
|
||||||
|
local language = 'en'
|
||||||
|
local catalogs = {}
|
||||||
|
|
||||||
local function render_template(name, template, scope)
|
function ctx.set_language(langs)
|
||||||
|
for _, lang in ipairs(langs) do
|
||||||
|
if i18n.supported(lang) then
|
||||||
|
language = lang
|
||||||
|
catalogs = {}
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctx.i18n(pkg)
|
||||||
|
local cat = catalogs[pkg] or i18n.load(language, pkg)
|
||||||
|
if pkg then catalogs[pkg] = cat end
|
||||||
|
return cat
|
||||||
|
end
|
||||||
|
|
||||||
|
local function render_template(name, template, scope, pkg)
|
||||||
scope = scope or {}
|
scope = scope or {}
|
||||||
|
local t = ctx.i18n(pkg)
|
||||||
|
|
||||||
local locals = {
|
local locals = {
|
||||||
renderer = ctx,
|
renderer = ctx,
|
||||||
translate = ctx.translate,
|
i18n = ctx.i18n,
|
||||||
translatef = ctx.translatef,
|
translate = t.translate,
|
||||||
_translate = ctx._translate,
|
translatef = t.translatef,
|
||||||
|
_translate = t._translate,
|
||||||
include = function(name)
|
include = function(name)
|
||||||
ctx.render(name, scope)
|
ctx.render(name, scope, pkg)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
setfenv(template, setmetatable({}, {
|
setfenv(template, setmetatable({}, {
|
||||||
__index = function(tbl, key)
|
__index = function(tbl, key)
|
||||||
return scope[key] or env[key] or locals[key]
|
return scope[key] or locals[key] or env[key]
|
||||||
end
|
end
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -46,7 +66,7 @@ function renderer(env)
|
|||||||
--- Render a certain template.
|
--- Render a certain template.
|
||||||
-- @param name Template name
|
-- @param name Template name
|
||||||
-- @param scope Scope to assign to template (optional)
|
-- @param scope Scope to assign to template (optional)
|
||||||
function ctx.render(name, scope)
|
function ctx.render(name, scope, pkg)
|
||||||
local sourcefile = viewdir .. name .. ".html"
|
local sourcefile = viewdir .. name .. ".html"
|
||||||
local template, _, err = tparser.parse(sourcefile)
|
local template, _, err = tparser.parse(sourcefile)
|
||||||
|
|
||||||
@ -54,45 +74,19 @@ function renderer(env)
|
|||||||
"Error while parsing template '" .. sourcefile .. "':\n" ..
|
"Error while parsing template '" .. sourcefile .. "':\n" ..
|
||||||
(err or "Unknown syntax error"))
|
(err or "Unknown syntax error"))
|
||||||
|
|
||||||
render_template(name, template, scope)
|
render_template(name, template, scope, pkg)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Render a template from a string.
|
--- Render a template from a string.
|
||||||
-- @param template Template string
|
-- @param template Template string
|
||||||
-- @param scope Scope to assign to template (optional)
|
-- @param scope Scope to assign to template (optional)
|
||||||
function ctx.render_string(str, scope)
|
function ctx.render_string(str, scope, pkg)
|
||||||
local template, _, err = tparser.parse_string(str)
|
local template, _, err = tparser.parse_string(str)
|
||||||
|
|
||||||
assert(template, "Error while parsing template:\n" ..
|
assert(template, "Error while parsing template:\n" ..
|
||||||
(err or "Unknown syntax error"))
|
(err or "Unknown syntax error"))
|
||||||
|
|
||||||
render_template('(local)', template, scope)
|
render_template('(local)', template, scope, pkg)
|
||||||
end
|
|
||||||
|
|
||||||
function ctx.setlanguage(lang)
|
|
||||||
lang = lang:gsub("_", "-")
|
|
||||||
if not lang then return false end
|
|
||||||
|
|
||||||
if lang ~= 'en' and not fs.access(i18ndir .. "gluon-web." .. lang .. ".lmo") then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return tparser.load_catalog(lang, i18ndir)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Returns a translated string, or nil if none is found
|
|
||||||
function ctx._translate(key)
|
|
||||||
return (tparser.translate(key))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Returns a translated string, or the original string if none is found
|
|
||||||
function ctx.translate(key)
|
|
||||||
return tparser.translate(key) or key
|
|
||||||
end
|
|
||||||
|
|
||||||
function ctx.translatef(key, ...)
|
|
||||||
local t = ctx.translate(key)
|
|
||||||
return t:format(...)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
@ -23,15 +23,12 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <fnmatch.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
|
|
||||||
struct lmo_entry {
|
struct lmo_entry {
|
||||||
@ -41,28 +38,6 @@ struct lmo_entry {
|
|||||||
uint32_t length;
|
uint32_t length;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
typedef struct lmo_entry lmo_entry_t;
|
|
||||||
|
|
||||||
|
|
||||||
struct lmo_archive {
|
|
||||||
size_t length;
|
|
||||||
const lmo_entry_t *index;
|
|
||||||
char *data;
|
|
||||||
const char *end;
|
|
||||||
struct lmo_archive *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct lmo_archive lmo_archive_t;
|
|
||||||
|
|
||||||
|
|
||||||
struct lmo_catalog {
|
|
||||||
char lang[6];
|
|
||||||
struct lmo_archive *archives;
|
|
||||||
struct lmo_catalog *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct lmo_catalog lmo_catalog_t;
|
|
||||||
|
|
||||||
|
|
||||||
static inline uint16_t get_le16(const void *data) {
|
static inline uint16_t get_le16(const void *data) {
|
||||||
const uint8_t *d = data;
|
const uint8_t *d = data;
|
||||||
@ -122,12 +97,13 @@ static uint32_t sfh_hash(const void *input, size_t len)
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
static lmo_archive_t * lmo_open(const char *file)
|
bool lmo_load(lmo_catalog_t *cat, const char *file)
|
||||||
{
|
{
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
lmo_archive_t *ar = NULL;
|
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
|
||||||
|
cat->data = MAP_FAILED;
|
||||||
|
|
||||||
fd = open(file, O_RDONLY|O_CLOEXEC);
|
fd = open(file, O_RDONLY|O_CLOEXEC);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
goto err;
|
goto err;
|
||||||
@ -135,110 +111,42 @@ static lmo_archive_t * lmo_open(const char *file)
|
|||||||
if (fstat(fd, &s))
|
if (fstat(fd, &s))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if ((ar = calloc(1, sizeof(*ar))) != NULL) {
|
cat->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
ar->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
|
|
||||||
if (ar->data == MAP_FAILED)
|
if (cat->data == MAP_FAILED)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ar->end = ar->data + s.st_size;
|
cat->end = cat->data + s.st_size;
|
||||||
|
|
||||||
uint32_t idx_offset = get_be32(ar->end - sizeof(uint32_t));
|
uint32_t idx_offset = get_be32(cat->end - sizeof(uint32_t));
|
||||||
ar->index = (const lmo_entry_t *)(ar->data + idx_offset);
|
cat->index = (const lmo_entry_t *)(cat->data + idx_offset);
|
||||||
|
|
||||||
if ((const char *)ar->index > (ar->end - sizeof(uint32_t)))
|
if ((const char *)cat->index > (cat->end - sizeof(uint32_t)))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ar->length = (ar->end - sizeof(uint32_t) - (const char *)ar->index) / sizeof(lmo_entry_t);
|
cat->length = (cat->end - sizeof(uint32_t) - (const char *)cat->index) / sizeof(lmo_entry_t);
|
||||||
|
|
||||||
return ar;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (ar != NULL) {
|
if (cat->data != MAP_FAILED)
|
||||||
if ((ar->data != NULL) && (ar->data != MAP_FAILED))
|
munmap(cat->data, cat->end - cat->data);
|
||||||
munmap(ar->data, ar->end - ar->data);
|
|
||||||
|
|
||||||
free(ar);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static lmo_catalog_t *lmo_catalogs;
|
|
||||||
static lmo_catalog_t *lmo_active_catalog;
|
|
||||||
|
|
||||||
bool lmo_change_catalog(const char *lang)
|
|
||||||
{
|
|
||||||
lmo_catalog_t *cat;
|
|
||||||
|
|
||||||
for (cat = lmo_catalogs; cat; cat = cat->next) {
|
|
||||||
if (!strncmp(cat->lang, lang, sizeof(cat->lang))) {
|
|
||||||
lmo_active_catalog = cat;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lmo_load_catalog(const char *lang, const char *dir)
|
void lmo_unload(lmo_catalog_t *cat)
|
||||||
{
|
{
|
||||||
DIR *dh = NULL;
|
if (cat->data != MAP_FAILED)
|
||||||
char pattern[16];
|
munmap(cat->data, cat->end - cat->data);
|
||||||
char path[PATH_MAX];
|
|
||||||
struct dirent *de = NULL;
|
|
||||||
|
|
||||||
lmo_archive_t *ar = NULL;
|
|
||||||
lmo_catalog_t *cat = NULL;
|
|
||||||
|
|
||||||
if (lmo_change_catalog(lang))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!(dh = opendir(dir)))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (!(cat = calloc(1, sizeof(*cat))))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
|
|
||||||
snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
|
|
||||||
|
|
||||||
while ((de = readdir(dh)) != NULL) {
|
|
||||||
if (!fnmatch(pattern, de->d_name, 0)) {
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
|
|
||||||
ar = lmo_open(path);
|
|
||||||
|
|
||||||
if (ar) {
|
|
||||||
ar->next = cat->archives;
|
|
||||||
cat->archives = ar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(dh);
|
|
||||||
|
|
||||||
cat->next = lmo_catalogs;
|
|
||||||
lmo_catalogs = cat;
|
|
||||||
|
|
||||||
lmo_active_catalog = cat;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
err:
|
|
||||||
if (dh)
|
|
||||||
closedir(dh);
|
|
||||||
free(cat);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lmo_compare_entry(const void *a, const void *b)
|
static int lmo_compare_entry(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
@ -253,34 +161,26 @@ static int lmo_compare_entry(const void *a, const void *b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const lmo_entry_t * lmo_find_entry(const lmo_archive_t *ar, uint32_t hash)
|
static const lmo_entry_t * lmo_find_entry(const lmo_catalog_t *cat, uint32_t hash)
|
||||||
{
|
{
|
||||||
lmo_entry_t key;
|
lmo_entry_t key;
|
||||||
key.key_id = htonl(hash);
|
key.key_id = htonl(hash);
|
||||||
|
|
||||||
return bsearch(&key, ar->index, ar->length, sizeof(lmo_entry_t), lmo_compare_entry);
|
return bsearch(&key, cat->index, cat->length, sizeof(lmo_entry_t), lmo_compare_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lmo_translate(const char *key, size_t keylen, char **out, size_t *outlen)
|
bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen)
|
||||||
{
|
{
|
||||||
if (!lmo_active_catalog)
|
uint32_t hash = sfh_hash(key, keylen);
|
||||||
|
const lmo_entry_t *e = lmo_find_entry(cat, hash);
|
||||||
|
if (!e)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32_t hash = sfh_hash(key, keylen);
|
*out = cat->data + ntohl(e->offset);
|
||||||
|
|
||||||
for (const lmo_archive_t *ar = lmo_active_catalog->archives; ar; ar = ar->next) {
|
|
||||||
const lmo_entry_t *e = lmo_find_entry(ar, hash);
|
|
||||||
if (!e)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
*out = ar->data + ntohl(e->offset);
|
|
||||||
*outlen = ntohl(e->length);
|
*outlen = ntohl(e->length);
|
||||||
|
|
||||||
if (*out + *outlen > ar->end)
|
if (*out + *outlen > cat->end)
|
||||||
continue;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
@ -24,7 +24,21 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
bool lmo_load_catalog(const char *lang, const char *dir);
|
typedef struct lmo_entry lmo_entry_t;
|
||||||
bool lmo_translate(const char *key, size_t keylen, char **out, size_t *outlen);
|
|
||||||
|
|
||||||
|
struct lmo_catalog {
|
||||||
|
size_t length;
|
||||||
|
const lmo_entry_t *index;
|
||||||
|
char *data;
|
||||||
|
const char *end;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct lmo_catalog lmo_catalog_t;
|
||||||
|
|
||||||
|
|
||||||
|
bool lmo_load(lmo_catalog_t *cat, const char *file);
|
||||||
|
void lmo_unload(lmo_catalog_t *cat);
|
||||||
|
bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
#define TEMPLATE_LUALIB_META "gluon.web.template.parser"
|
#define TEMPLATE_CATALOG "gluon.web.template.parser.catalog"
|
||||||
|
|
||||||
|
|
||||||
static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname)
|
static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname)
|
||||||
@ -87,40 +87,64 @@ static int template_L_pcdata(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int template_L_load_catalog(lua_State *L) {
|
static int template_L_load_catalog(lua_State *L)
|
||||||
const char *lang = luaL_optstring(L, 1, "en");
|
{
|
||||||
const char *dir = luaL_checkstring(L, 2);
|
const char *file = luaL_checkstring(L, 1);
|
||||||
lua_pushboolean(L, lmo_load_catalog(lang, dir));
|
|
||||||
return 1;
|
lmo_catalog_t *cat = lua_newuserdata(L, sizeof(*cat));
|
||||||
|
if (!lmo_load(cat, file)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int template_L_translate(lua_State *L) {
|
luaL_getmetatable(L, TEMPLATE_CATALOG);
|
||||||
size_t len;
|
lua_setmetatable(L, -2);
|
||||||
char *tr;
|
|
||||||
size_t trlen;
|
|
||||||
const char *key = luaL_checklstring(L, 1, &len);
|
|
||||||
|
|
||||||
if (lmo_translate(key, len, &tr, &trlen))
|
|
||||||
lua_pushlstring(L, tr, trlen);
|
|
||||||
else
|
|
||||||
lua_pushnil(L);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int template_catalog_call(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t inlen, outlen;
|
||||||
|
lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG);
|
||||||
|
const char *in = luaL_checklstring(L, 2, &inlen), *out;
|
||||||
|
if (!lmo_translate(cat, in, inlen, &out, &outlen))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
lua_pushlstring(L, out, outlen);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int template_catalog_gc(lua_State *L)
|
||||||
|
{
|
||||||
|
lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG);
|
||||||
|
lmo_unload(cat);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* module table */
|
|
||||||
static const luaL_reg R[] = {
|
static const luaL_reg R[] = {
|
||||||
{ "parse", template_L_parse },
|
{ "parse", template_L_parse },
|
||||||
{ "parse_string", template_L_parse_string },
|
{ "parse_string", template_L_parse_string },
|
||||||
{ "pcdata", template_L_pcdata },
|
{ "pcdata", template_L_pcdata },
|
||||||
{ "load_catalog", template_L_load_catalog },
|
{ "load_catalog", template_L_load_catalog },
|
||||||
{ "translate", template_L_translate },
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const luaL_reg template_catalog_methods[] = {
|
||||||
|
{ "__call", template_catalog_call },
|
||||||
|
{ "__gc", template_catalog_gc },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__ ((visibility("default")))
|
__attribute__ ((visibility("default")))
|
||||||
LUALIB_API int luaopen_gluon_web_template_parser(lua_State *L) {
|
LUALIB_API int luaopen_gluon_web_template_parser(lua_State *L) {
|
||||||
luaL_register(L, TEMPLATE_LUALIB_META, R);
|
luaL_register(L, "gluon.web.template.parser", R);
|
||||||
|
|
||||||
|
luaL_newmetatable(L, TEMPLATE_CATALOG);
|
||||||
|
luaL_register(L, NULL, template_catalog_methods);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user