From: Steven Barth Date: Tue, 27 May 2008 08:14:24 +0000 (+0000) Subject: * Separated the MVC-Webframework from core X-Git-Tag: 0.8.0~984 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=b8124f18f88fe11158eb122a1e362cfbd512cf4f;p=oweals%2Fluci.git * Separated the MVC-Webframework from core * Separated i18n from core to prepare translations --- diff --git a/contrib/package/luci/Makefile b/contrib/package/luci/Makefile index 2458ebd28..c75247b55 100644 --- a/contrib/package/luci/Makefile +++ b/contrib/package/luci/Makefile @@ -46,19 +46,32 @@ endef define Package/luci $(call Package/luci/template) MENU:=1 - DEPENDS:=+lua +luaposix +luci-addons + DEPENDS:=+lua +luaposix endef -define Package/luci/conffiles +define Package/luci/install + $(call Package/luci/install/template,$(1),core) +endef + + +### Web Packages ### +define Package/luci-web + $(call Package/luci/template) + DEPENDS:=luci +luci-addons + TITLE:=LuCI - Webframework +endef + +define Package/luci-web/conffiles /etc/config/luci endef -define Package/luci/install - $(call Package/luci/install/template,$(1),core) +define Package/luci-web/install + $(call Package/luci/install/template,$(1),web) $(call Package/luci/install/template,$(1),themes/fledermaus) endef + ### Community Packages ### define Package/luci-ff-halle @@ -111,7 +124,7 @@ endef define Package/luci-mod-admin-core $(call Package/luci/template) - DEPENDS:=luci + DEPENDS:=luci +luci-web TITLE:=Core administrative pages endef diff --git a/core/root/etc/config/luci b/core/root/etc/config/luci deleted file mode 100644 index 87170a9b1..000000000 --- a/core/root/etc/config/luci +++ /dev/null @@ -1,39 +0,0 @@ -config core main - option lang de - option mediaurlbase /luci/fledermaus - option resourcebase /luci/resources - -config core brand - option title "OpenWRT Kamikaze" - option subtitle Freifunkedition - option firmware "OpenWRT Kamikaze" - option distro "Freifunk Firmware 2.x" - -config extern flash_keep - option uci "/etc/config" - option dropbear "/etc/dropbear" - option openvpn "/etc/openvpn" - option passwd "/etc/passwd" - option ipkg "/etc/ipkg.conf" - option httpd "/etc/httpd.conf" - option firewall "/etc/firewall.user" - -config event uci_oncommit - option network "/etc/init.d/network restart" - option wireless "/etc/init.d/network restart" - option olsr "/sbin/reboot" - option dhcp "/etc/init.d/dnsmasq restart" - option luci_fw "/etc/init.d/luci_fw restart" - option dropbear "/etc/init.d/dropbear restart" - option httpd "/etc/init.d/httpd restart" - option fstab "/etc/init.d/fstab restart" - option qos "/etc/init.d/qos restart" - option luci_splash "/etc/init.d/luci_splash restart" - option freifunk "/etc/init.d/luci_freifunk restart" - -config internal languages - option de "Deutsch" - option en "English" - -config internal themes - option Fledermaus "/luci/fledermaus" diff --git a/core/root/www/resources/cbi.js b/core/root/www/resources/cbi.js deleted file mode 100644 index a3a47aa45..000000000 --- a/core/root/www/resources/cbi.js +++ /dev/null @@ -1,42 +0,0 @@ -var cbi_d = {}; - -function cbi_d_add(field, target, value) { - if (!cbi_d[target]) { - cbi_d[target] = {}; - } - if (!cbi_d[target][value]) { - cbi_d[target][value] = []; - } - cbi_d[target][value].push(field); -} - -function cbi_d_update(target) { - if (!cbi_d[target]) { - return; - } - - for (var x in cbi_d[target]) { - for (var i=0; i - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- -module("luci.cbi", package.seeall) - -require("luci.template") -require("luci.util") -require("luci.http") -require("luci.model.uci") - -local class = luci.util.class -local instanceof = luci.util.instanceof - --- Loads a CBI map from given file, creating an environment and returns it -function load(cbimap) - require("luci.fs") - require("luci.i18n") - require("luci.config") - require("luci.sys") - - local cbidir = luci.sys.libpath() .. "/model/cbi/" - local func, err = loadfile(cbidir..cbimap..".lua") - - if not func then - return nil - end - - luci.i18n.loadc("cbi") - - luci.util.resfenv(func) - luci.util.updfenv(func, luci.cbi) - luci.util.extfenv(func, "translate", luci.i18n.translate) - - local map = func() - - if not instanceof(map, Map) then - error("CBI map returns no valid map object!") - return nil - end - - return map -end - --- Node pseudo abstract class -Node = class() - -function Node.__init__(self, title, description) - self.children = {} - self.title = title or "" - self.description = description or "" - self.template = "cbi/node" -end - --- Append child nodes -function Node.append(self, obj) - table.insert(self.children, obj) -end - --- Parse this node and its children -function Node.parse(self, ...) - for k, child in ipairs(self.children) do - child:parse(...) - end -end - --- Render this node -function Node.render(self, scope) - scope = scope or {} - scope.self = self - - luci.template.render(self.template, scope) -end - --- Render the children -function Node.render_children(self, ...) - for k, node in ipairs(self.children) do - node:render(...) - end -end - - ---[[ -A simple template element -]]-- -Template = class(Node) - -function Template.__init__(self, template) - Node.__init__(self) - self.template = template -end - - ---[[ -Map - A map describing a configuration file -]]-- -Map = class(Node) - -function Map.__init__(self, config, ...) - Node.__init__(self, ...) - self.config = config - self.template = "cbi/map" - self.uci = luci.model.uci.Session() - self.ucidata, self.uciorder = self.uci:sections(self.config) - if not self.ucidata or not self.uciorder then - error("Unable to read UCI data: " .. self.config) - end -end - --- Use optimized UCI writing -function Map.parse(self, ...) - self.uci:t_load(self.config) - Node.parse(self, ...) - self.uci:t_save(self.config) -end - --- Creates a child section -function Map.section(self, class, ...) - if instanceof(class, AbstractSection) then - local obj = class(self, ...) - self:append(obj) - return obj - else - error("class must be a descendent of AbstractSection") - end -end - --- UCI add -function Map.add(self, sectiontype) - local name = self.uci:t_add(self.config, sectiontype) - if name then - self.ucidata[name] = {} - self.ucidata[name][".type"] = sectiontype - table.insert(self.uciorder, name) - end - return name -end - --- UCI set -function Map.set(self, section, option, value) - local stat = self.uci:t_set(self.config, section, option, value) - if stat then - local val = self.uci:t_get(self.config, section, option) - if option then - self.ucidata[section][option] = val - else - if not self.ucidata[section] then - self.ucidata[section] = {} - end - self.ucidata[section][".type"] = val - table.insert(self.uciorder, section) - end - end - return stat -end - --- UCI del -function Map.del(self, section, option) - local stat = self.uci:t_del(self.config, section, option) - if stat then - if option then - self.ucidata[section][option] = nil - else - self.ucidata[section] = nil - for i, k in ipairs(self.uciorder) do - if section == k then - table.remove(self.uciorder, i) - end - end - end - end - return stat -end - --- UCI get (cached) -function Map.get(self, section, option) - if not section then - return self.ucidata, self.uciorder - elseif option and self.ucidata[section] then - return self.ucidata[section][option] - else - return self.ucidata[section] - end -end - - ---[[ -AbstractSection -]]-- -AbstractSection = class(Node) - -function AbstractSection.__init__(self, map, sectiontype, ...) - Node.__init__(self, ...) - self.sectiontype = sectiontype - self.map = map - self.config = map.config - self.optionals = {} - - self.optional = true - self.addremove = false - self.dynamic = false -end - --- Appends a new option -function AbstractSection.option(self, class, ...) - if instanceof(class, AbstractValue) then - local obj = class(self.map, ...) - self:append(obj) - return obj - else - error("class must be a descendent of AbstractValue") - end -end - --- Parse optional options -function AbstractSection.parse_optionals(self, section) - if not self.optional then - return - end - - self.optionals[section] = {} - - local field = luci.http.formvalue("cbi.opt."..self.config.."."..section) - for k,v in ipairs(self.children) do - if v.optional and not v:cfgvalue(section) then - if field == v.option then - field = nil - else - table.insert(self.optionals[section], v) - end - end - end - - if field and #field > 0 and self.dynamic then - self:add_dynamic(field) - end -end - --- Add a dynamic option -function AbstractSection.add_dynamic(self, field, optional) - local o = self:option(Value, field, field) - o.optional = optional -end - --- Parse all dynamic options -function AbstractSection.parse_dynamic(self, section) - if not self.dynamic then - return - end - - local arr = luci.util.clone(self:cfgvalue(section)) - local form = luci.http.formvaluetable("cbid."..self.config.."."..section) - for k, v in pairs(form) do - arr[k] = v - end - - for key,val in pairs(arr) do - local create = true - - for i,c in ipairs(self.children) do - if c.option == key then - create = false - end - end - - if create and key:sub(1, 1) ~= "." then - self:add_dynamic(key, true) - end - end -end - --- Returns the section's UCI table -function AbstractSection.cfgvalue(self, section) - return self.map:get(section) -end - --- Removes the section -function AbstractSection.remove(self, section) - return self.map:del(section) -end - --- Creates the section -function AbstractSection.create(self, section) - return self.map:set(section, nil, self.sectiontype) -end - - - ---[[ -NamedSection - A fixed configuration section defined by its name -]]-- -NamedSection = class(AbstractSection) - -function NamedSection.__init__(self, map, section, ...) - AbstractSection.__init__(self, map, ...) - self.template = "cbi/nsection" - - self.section = section - self.addremove = false -end - -function NamedSection.parse(self) - local s = self.section - local active = self:cfgvalue(s) - - - if self.addremove then - local path = self.config.."."..s - if active then -- Remove the section - if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then - return - end - else -- Create and apply default values - if luci.http.formvalue("cbi.cns."..path) and self:create(s) then - for k,v in pairs(self.children) do - v:write(s, v.default) - end - end - end - end - - if active then - AbstractSection.parse_dynamic(self, s) - if luci.http.formvalue("cbi.submit") then - Node.parse(self, s) - end - AbstractSection.parse_optionals(self, s) - end -end - - ---[[ -TypedSection - A (set of) configuration section(s) defined by the type - addremove: Defines whether the user can add/remove sections of this type - anonymous: Allow creating anonymous sections - validate: a validation function returning nil if the section is invalid -]]-- -TypedSection = class(AbstractSection) - -function TypedSection.__init__(self, ...) - AbstractSection.__init__(self, ...) - self.template = "cbi/tsection" - self.deps = {} - self.excludes = {} - - self.anonymous = false -end - --- Return all matching UCI sections for this TypedSection -function TypedSection.cfgsections(self) - local sections = {} - local map, order = self.map:get() - - for i, k in ipairs(order) do - if map[k][".type"] == self.sectiontype then - if self:checkscope(k) then - table.insert(sections, k) - end - end - end - - return sections -end - --- Creates a new section of this type with the given name (or anonymous) -function TypedSection.create(self, name) - if name then - self.map:set(name, nil, self.sectiontype) - else - name = self.map:add(self.sectiontype) - end - - for k,v in pairs(self.children) do - if v.default then - self.map:set(name, v.option, v.default) - end - end -end - --- Limits scope to sections that have certain option => value pairs -function TypedSection.depends(self, option, value) - table.insert(self.deps, {option=option, value=value}) -end - --- Excludes several sections by name -function TypedSection.exclude(self, field) - self.excludes[field] = true -end - -function TypedSection.parse(self) - if self.addremove then - -- Create - local crval = "cbi.cts." .. self.config .. "." .. self.sectiontype - local name = luci.http.formvalue(crval) - if self.anonymous then - if name then - self:create() - end - else - if name then - -- Ignore if it already exists - if self:cfgvalue(name) then - name = nil; - end - - name = self:checkscope(name) - - if not name then - self.err_invalid = true - end - - if name and name:len() > 0 then - self:create(name) - end - end - end - - -- Remove - crval = "cbi.rts." .. self.config - name = luci.http.formvaluetable(crval) - for k,v in pairs(name) do - if self:cfgvalue(k) and self:checkscope(k) then - self:remove(k) - end - end - end - - for i, k in ipairs(self:cfgsections()) do - AbstractSection.parse_dynamic(self, k) - if luci.http.formvalue("cbi.submit") then - Node.parse(self, k) - end - AbstractSection.parse_optionals(self, k) - end -end - --- Verifies scope of sections -function TypedSection.checkscope(self, section) - -- Check if we are not excluded - if self.excludes[section] then - return nil - end - - -- Check if at least one dependency is met - if #self.deps > 0 and self:cfgvalue(section) then - local stat = false - - for k, v in ipairs(self.deps) do - if self:cfgvalue(section)[v.option] == v.value then - stat = true - end - end - - if not stat then - return nil - end - end - - return self:validate(section) -end - - --- Dummy validate function -function TypedSection.validate(self, section) - return section -end - - ---[[ -AbstractValue - An abstract Value Type - null: Value can be empty - valid: A function returning the value if it is valid otherwise nil - depends: A table of option => value pairs of which one must be true - default: The default value - size: The size of the input fields - rmempty: Unset value if empty - optional: This value is optional (see AbstractSection.optionals) -]]-- -AbstractValue = class(Node) - -function AbstractValue.__init__(self, map, option, ...) - Node.__init__(self, ...) - self.option = option - self.map = map - self.config = map.config - self.tag_invalid = {} - self.deps = {} - - self.rmempty = false - self.default = nil - self.size = nil - self.optional = false -end - --- Add a dependencie to another section field -function AbstractValue.depends(self, field, value) - table.insert(self.deps, {field=field, value=value}) -end - --- Return whether this object should be created -function AbstractValue.formcreated(self, section) - local key = "cbi.opt."..self.config.."."..section - return (luci.http.formvalue(key) == self.option) -end - --- Returns the formvalue for this object -function AbstractValue.formvalue(self, section) - local key = "cbid."..self.map.config.."."..section.."."..self.option - return luci.http.formvalue(key) -end - -function AbstractValue.parse(self, section) - local fvalue = self:formvalue(section) - - if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI - fvalue = self:validate(fvalue) - if not fvalue then - self.tag_invalid[section] = true - end - if fvalue and not (fvalue == self:cfgvalue(section)) then - self:write(section, fvalue) - end - else -- Unset the UCI or error - if self.rmempty or self.optional then - self:remove(section) - end - end -end - --- Render if this value exists or if it is mandatory -function AbstractValue.render(self, s, scope) - if not self.optional or self:cfgvalue(s) or self:formcreated(s) then - scope = scope or {} - scope.section = s - Node.render(self, scope) - end -end - --- Return the UCI value of this object -function AbstractValue.cfgvalue(self, section) - return self.map:get(section, self.option) -end - --- Validate the form value -function AbstractValue.validate(self, value) - return value -end - --- Write to UCI -function AbstractValue.write(self, section, value) - return self.map:set(section, self.option, value) -end - --- Remove from UCI -function AbstractValue.remove(self, section) - return self.map:del(section, self.option) -end - - - - ---[[ -Value - A one-line value - maxlength: The maximum length - isnumber: The value must be a valid (floating point) number - isinteger: The value must be a valid integer - ispositive: The value must be positive (and a number) -]]-- -Value = class(AbstractValue) - -function Value.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/value" - - self.maxlength = nil - self.isnumber = false - self.isinteger = false -end - --- This validation is a bit more complex -function Value.validate(self, val) - if self.maxlength and tostring(val):len() > self.maxlength then - val = nil - end - - return luci.util.validate(val, self.isnumber, self.isinteger) -end - - --- DummyValue - This does nothing except being there -DummyValue = class(AbstractValue) - -function DummyValue.__init__(self, map, ...) - AbstractValue.__init__(self, map, ...) - self.template = "cbi/dvalue" - self.value = nil -end - -function DummyValue.parse(self) - -end - -function DummyValue.render(self, s) - luci.template.render(self.template, {self=self, section=s}) -end - - ---[[ -Flag - A flag being enabled or disabled -]]-- -Flag = class(AbstractValue) - -function Flag.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/fvalue" - - self.enabled = "1" - self.disabled = "0" -end - --- A flag can only have two states: set or unset -function Flag.parse(self, section) - local fvalue = self:formvalue(section) - - if fvalue then - fvalue = self.enabled - else - fvalue = self.disabled - end - - if fvalue == self.enabled or (not self.optional and not self.rmempty) then - if not(fvalue == self:cfgvalue(section)) then - self:write(section, fvalue) - end - else - self:remove(section) - end -end - - - ---[[ -ListValue - A one-line value predefined in a list - widget: The widget that will be used (select, radio) -]]-- -ListValue = class(AbstractValue) - -function ListValue.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/lvalue" - self.keylist = {} - self.vallist = {} - - self.size = 1 - self.widget = "select" -end - -function ListValue.value(self, key, val) - val = val or key - table.insert(self.keylist, tostring(key)) - table.insert(self.vallist, tostring(val)) -end - -function ListValue.validate(self, val) - if luci.util.contains(self.keylist, val) then - return val - else - return nil - end -end - - - ---[[ -MultiValue - Multiple delimited values - widget: The widget that will be used (select, checkbox) - delimiter: The delimiter that will separate the values (default: " ") -]]-- -MultiValue = class(AbstractValue) - -function MultiValue.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/mvalue" - self.keylist = {} - self.vallist = {} - - self.widget = "checkbox" - self.delimiter = " " -end - -function MultiValue.value(self, key, val) - val = val or key - table.insert(self.keylist, tostring(key)) - table.insert(self.vallist, tostring(val)) -end - -function MultiValue.valuelist(self, section) - local val = self:cfgvalue(section) - - if not(type(val) == "string") then - return {} - end - - return luci.util.split(val, self.delimiter) -end - -function MultiValue.validate(self, val) - if not(type(val) == "string") then - return nil - end - - local result = "" - - for value in val:gmatch("[^\n]+") do - if luci.util.contains(self.keylist, value) then - result = result .. self.delimiter .. value - end - end - - if result:len() > 0 then - return result:sub(self.delimiter:len() + 1) - else - return nil - end -end \ No newline at end of file diff --git a/core/src/config.lua b/core/src/config.lua deleted file mode 100644 index 854b12814..000000000 --- a/core/src/config.lua +++ /dev/null @@ -1,48 +0,0 @@ ---[[ -LuCI - Configuration - -Description: -Some LuCI configuration values read from uci file "luci" - - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- - -module("luci.config", package.seeall) -require("luci.model.uci") -require("luci.util") -require("luci.sys") - --- Warning! This is only for fallback and compatibility purporses! -- -main = {} - --- This is where stylesheets and images go -main.mediaurlbase = "/luci/media" - --- Does anybody think about browser autodetect here? --- Too bad busybox doesn't populate HTTP_ACCEPT_LANGUAGE -main.lang = "de" - - --- Now overwrite with UCI values -local ucidata = luci.model.uci.sections("luci") -if ucidata then - luci.util.update(luci.config, ucidata) -end \ No newline at end of file diff --git a/core/src/dispatcher.lua b/core/src/dispatcher.lua deleted file mode 100644 index 103260c48..000000000 --- a/core/src/dispatcher.lua +++ /dev/null @@ -1,278 +0,0 @@ ---[[ -LuCI - Dispatcher - -Description: -The request dispatcher and module dispatcher generators - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- -module("luci.dispatcher", package.seeall) -require("luci.http") -require("luci.sys") -require("luci.fs") - --- Local dispatch database -local tree = {nodes={}} - --- Index table -local index = {} - --- Global request object -request = {} - --- Active dispatched node -dispatched = nil - --- Status fields -built_index = false -built_tree = false - - --- Builds a URL -function build_url(...) - return luci.http.dispatcher() .. "/" .. table.concat(arg, "/") -end - --- Sends a 404 error code and renders the "error404" template if available -function error404(message) - luci.http.status(404, "Not Found") - message = message or "Not Found" - - require("luci.template") - if not pcall(luci.template.render, "error404") then - luci.http.prepare_content("text/plain") - print(message) - end - return false -end - --- Sends a 500 error code and renders the "error500" template if available -function error500(message) - luci.http.status(500, "Internal Server Error") - - require("luci.template") - if not pcall(luci.template.render, "error500", {message=message}) then - luci.http.prepare_content("text/plain") - print(message) - end - return false -end - --- Creates a request object for dispatching -function httpdispatch() - local pathinfo = luci.http.env.PATH_INFO or "" - local c = tree - - for s in pathinfo:gmatch("([%w_]+)") do - table.insert(request, s) - end - - dispatch() -end - --- Dispatches a request -function dispatch() - if not built_tree then - createtree() - end - - local c = tree - local track = {} - - for i, s in ipairs(request) do - c = c.nodes[s] - if not c then - break - end - - for k, v in pairs(c) do - track[k] = v - end - end - - - if track.i18n then - require("luci.i18n").loadc(track.i18n) - end - - if track.setgroup then - luci.sys.process.setgroup(track.setgroup) - end - - if track.setuser then - luci.sys.process.setuser(track.setuser) - end - - -- Init template engine - local tpl = require("luci.template") - tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end - tpl.viewns.controller = luci.http.dispatcher() - tpl.viewns.uploadctrl = luci.http.dispatcher_upload() - tpl.viewns.media = luci.config.main.mediaurlbase - tpl.viewns.resource = luci.config.main.resourcebase - - - if c and type(c.target) == "function" then - dispatched = c - - stat, err = pcall(c.target) - if not stat then - error500(err) - end - else - error404() - end -end - --- Generates the dispatching tree -function createindex() - index = {} - local path = luci.sys.libpath() .. "/controller/" - local suff = ".lua" - - if pcall(require, "fastindex") then - createindex_fastindex(path, suff) - else - createindex_plain(path, suff) - end - - built_index = true -end - --- Uses fastindex to create the dispatching tree -function createindex_fastindex(path, suffix) - local fi = fastindex.new("index") - fi.add(path .. "*" .. suffix) - fi.add(path .. "*/*" .. suffix) - fi.scan() - - for k, v in pairs(fi.indexes) do - index[v[2]] = v[1] - end -end - --- Calls the index function of all available controllers -function createindex_plain(path, suffix) - local controllers = luci.util.combine( - luci.fs.glob(path .. "*" .. suffix) or {}, - luci.fs.glob(path .. "*/*" .. suffix) or {} - ) - - for i,c in ipairs(controllers) do - c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".") - stat, mod = pcall(require, c) - - if stat and mod and type(mod.index) == "function" then - index[c] = mod.index - end - end -end - --- Creates the dispatching tree from the index -function createtree() - if not built_index then - createindex() - end - - for k, v in pairs(index) do - luci.util.updfenv(v, _M) - - local stat, mod = pcall(require, k) - if stat then - luci.util.updfenv(v, mod) - end - - pcall(v) - end - - built_tree = true -end - --- Shortcut for creating a dispatching node -function entry(path, target, title, order, add) - add = add or {} - - local c = node(path) - c.target = target - c.title = title - c.order = order - - for k,v in pairs(add) do - c[k] = v - end - - return c -end - --- Fetch a dispatching node -function node(...) - local c = tree - - if arg[1] and type(arg[1]) == "table" then - arg = arg[1] - end - - for k,v in ipairs(arg) do - if not c.nodes[v] then - c.nodes[v] = {nodes={}} - end - - c = c.nodes[v] - end - - return c -end - --- Subdispatchers -- -function alias(...) - local req = arg - return function() - request = req - dispatch() - end -end - -function template(name) - require("luci.template") - return function() luci.template.render(name) end -end - -function cbi(model) - require("luci.cbi") - require("luci.template") - - return function() - local stat, res = pcall(luci.cbi.load, model) - if not stat then - error500(res) - return true - end - - local stat, err = pcall(res.parse, res) - if not stat then - error500(err) - return true - end - - luci.template.render("cbi/header") - res:render() - luci.template.render("cbi/footer") - end -end diff --git a/core/src/http.lua b/core/src/http.lua deleted file mode 100644 index fa8821c5a..000000000 --- a/core/src/http.lua +++ /dev/null @@ -1,36 +0,0 @@ ---[[ -LuCI - HTTP-Interaction - -Description: -HTTP-Header manipulator and form variable preprocessor - -FileId: -$Id$ - -ToDo: -- Cookie handling - -License: -Copyright 2008 Steven Barth - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- - -module("luci.http", package.seeall) - -if ENV and ENV.HASERLVER then - require("luci.sgi.haserl") -elseif webuci then - require("luci.sgi.webuci") -end \ No newline at end of file diff --git a/core/src/i18n.lua b/core/src/i18n.lua deleted file mode 100644 index 3a8a9a6c7..000000000 --- a/core/src/i18n.lua +++ /dev/null @@ -1,63 +0,0 @@ ---[[ -LuCI - Internationalisation - -Description: -A very minimalistic but yet effective internationalisation module - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- - -module("luci.i18n", package.seeall) -require("luci.sys") - -table = {} -i18ndir = luci.sys.libpath() .. "/i18n/" - --- Clears the translation table -function clear() - table = {} -end - --- Loads a translation and copies its data into the global translation table -function load(file) - local f = loadfile(i18ndir .. file) - if f then - setfenv(f, table) - f() - return true - else - return false - end -end - --- Same as load but autocompletes the filename with .LANG from config.lang -function loadc(file) - return load(file .. "." .. require("luci.config").main.lang) -end - --- Returns the i18n-value defined by "key" or if there is no such: "default" -function translate(key, default) - return table[key] or default -end - --- Translate shourtcut with sprintf/string.format inclusion -function translatef(key, default, ...) - return translate(key, default):format(...) -end \ No newline at end of file diff --git a/core/src/i18n/cbi.en b/core/src/i18n/cbi.en deleted file mode 100644 index 6069cec45..000000000 --- a/core/src/i18n/cbi.en +++ /dev/null @@ -1,4 +0,0 @@ -cbi_add = "Add entry" -cbi_del = "Remove entry" -cbi_invalid = "Error: Invalid input value" -cbi_addopt = "-- Field --" \ No newline at end of file diff --git a/core/src/i18n/default.en b/core/src/i18n/default.en deleted file mode 100644 index d76b770e9..000000000 --- a/core/src/i18n/default.en +++ /dev/null @@ -1,14 +0,0 @@ -add = "Add" -save = "Save" -reset = "Reset" - -load = "Load" -webif = "Webinterface" -public = "Public" -admin = "Administration" -apply = "Apply" -changes = "Changes" -revert = "Revert" - -config = "Configuration" -path = "Path" \ No newline at end of file diff --git a/core/src/template.lua b/core/src/template.lua deleted file mode 100644 index 369aa0a30..000000000 --- a/core/src/template.lua +++ /dev/null @@ -1,220 +0,0 @@ ---[[ -LuCI - Template Parser - -Description: -A template parser supporting includes, translations, Lua code blocks -and more. It can be used either as a compiler or as an interpreter. - -FileId: $Id$ - -License: -Copyright 2008 Steven Barth - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -]]-- -module("luci.template", package.seeall) - -require("luci.config") -require("luci.util") -require("luci.fs") -require("luci.http") - -viewdir = luci.sys.libpath() .. "/view/" - - --- Compile modes: --- none: Never compile, only use precompiled data from files --- memory: Always compile, do not save compiled files, ignore precompiled --- file: Compile on demand, save compiled files, update precompiled -compiler_mode = "memory" - - --- This applies to compiler modes "always" and "smart" --- --- Produce compiled lua code rather than lua sourcecode --- WARNING: Increases template size heavily!!! --- This produces the same bytecode as luac but does not have a strip option -compiler_enable_bytecode = false - - --- Define the namespace for template modules -viewns = { - write = io.write, - include = function(name) Template(name):render(getfenv(2)) end, -} - --- Compiles a given template into an executable Lua module -function compile(template) - -- Search all <% %> expressions (remember: Lua table indexes begin with #1) - local function expr_add(command) - table.insert(expr, command) - return "<%" .. tostring(#expr) .. "%>" - end - - -- As "expr" should be local, we have to assign it to the "expr_add" scope - local expr = {} - luci.util.extfenv(expr_add, "expr", expr) - - -- Save all expressiosn to table "expr" - template = template:gsub("<%%(.-)%%>", expr_add) - - local function sanitize(s) - s = luci.util.escape(s) - s = luci.util.escape(s, "'") - s = luci.util.escape(s, "\n") - return s - end - - -- Escape and sanitize all the template (all non-expressions) - template = sanitize(template) - - -- Template module header/footer declaration - local header = "write('" - local footer = "')" - - template = header .. template .. footer - - -- Replacements - local r_include = "')\ninclude('%s')\nwrite('" - local r_i18n = "'..translate('%1','%2')..'" - local r_pexec = "'..(%s or '')..'" - local r_exec = "')\n%s\nwrite('" - - -- Parse the expressions - for k,v in pairs(expr) do - local p = v:sub(1, 1) - local re = nil - if p == "+" then - re = r_include:format(sanitize(string.sub(v, 2))) - elseif p == ":" then - re = sanitize(v):gsub(":(.-) (.+)", r_i18n) - elseif p == "=" then - re = r_pexec:format(v:sub(2)) - else - re = r_exec:format(v) - end - template = template:gsub("<%%"..tostring(k).."%%>", re) - end - - if compiler_enable_bytecode then - tf = loadstring(template) - template = string.dump(tf) - end - - return template -end - --- Oldstyle render shortcut -function render(name, scope, ...) - scope = scope or getfenv(2) - local s, t = pcall(Template, name) - if not s then - error(t) - else - t:render(scope, ...) - end -end - - --- Template class -Template = luci.util.class() - --- Shared template cache to store templates in to avoid unnecessary reloading -Template.cache = {} - - --- Constructor - Reads and compiles the template on-demand -function Template.__init__(self, name) - if self.cache[name] then - self.template = self.cache[name] - else - self.template = nil - end - - -- Create a new namespace for this template - self.viewns = {} - - -- Copy over from general namespace - for k, v in pairs(viewns) do - self.viewns[k] = v - end - - -- If we have a cached template, skip compiling and loading - if self.template then - return - end - - -- Compile and build - local sourcefile = viewdir .. name .. ".htm" - local compiledfile = viewdir .. name .. ".lua" - local err - - if compiler_mode == "file" then - local tplmt = luci.fs.mtime(sourcefile) - local commt = luci.fs.mtime(compiledfile) - - -- Build if there is no compiled file or if compiled file is outdated - if ((commt == nil) and not (tplmt == nil)) - or (not (commt == nil) and not (tplmt == nil) and commt < tplmt) then - local source - source, err = luci.fs.readfile(sourcefile) - - if source then - local compiled = compile(source) - luci.fs.writefile(compiledfile, compiled) - self.template, err = loadstring(compiled) - end - else - self.template, err = loadfile(compiledfile) - end - - elseif compiler_mode == "none" then - self.template, err = loadfile(self.compiledfile) - - elseif compiler_mode == "memory" then - local source - source, err = luci.fs.readfile(sourcefile) - if source then - self.template, err = loadstring(compile(source)) - end - - end - - -- If we have no valid template throw error, otherwise cache the template - if not self.template then - error(err) - else - self.cache[name] = self.template - end -end - - --- Renders a template -function Template.render(self, scope) - scope = scope or getfenv(2) - - -- Save old environment - local oldfenv = getfenv(self.template) - - -- Put our predefined objects in the scope of the template - luci.util.resfenv(self.template) - luci.util.updfenv(self.template, scope) - luci.util.updfenv(self.template, self.viewns) - - -- Now finally render the thing - self.template() - - -- Reset environment - setfenv(self.template, oldfenv) -end diff --git a/core/src/view/cbi/dvalue.htm b/core/src/view/cbi/dvalue.htm deleted file mode 100644 index f54667def..000000000 --- a/core/src/view/cbi/dvalue.htm +++ /dev/null @@ -1,12 +0,0 @@ -<%+cbi/valueheader%> -<% if self.value then - if type(self.value) == "function" then %> - <%=self:value(section)%> -<% else %> - <%=self.value%> -<% end -else %> - <%=self:cfgvalue(section)%> -<% end %> -  -<%+cbi/valuefooter%> diff --git a/core/src/view/cbi/footer.htm b/core/src/view/cbi/footer.htm deleted file mode 100644 index 2acf710cd..000000000 --- a/core/src/view/cbi/footer.htm +++ /dev/null @@ -1,7 +0,0 @@ -
- - - -
- -<%+footer%> \ No newline at end of file diff --git a/core/src/view/cbi/full_valuefooter.htm b/core/src/view/cbi/full_valuefooter.htm deleted file mode 100644 index 6151a3a66..000000000 --- a/core/src/view/cbi/full_valuefooter.htm +++ /dev/null @@ -1,8 +0,0 @@ -
<%=self.description%> 
- - <% if self.tag_invalid[section] then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> - - <% if #self.deps > 0 then %><% end %> \ No newline at end of file diff --git a/core/src/view/cbi/full_valueheader.htm b/core/src/view/cbi/full_valueheader.htm deleted file mode 100644 index 062efa2dd..000000000 --- a/core/src/view/cbi/full_valueheader.htm +++ /dev/null @@ -1,3 +0,0 @@ -
"> -
<%=self.title%>
-
\ No newline at end of file diff --git a/core/src/view/cbi/fvalue.htm b/core/src/view/cbi/fvalue.htm deleted file mode 100644 index b609f1d4f..000000000 --- a/core/src/view/cbi/fvalue.htm +++ /dev/null @@ -1,3 +0,0 @@ -<%+cbi/valueheader%> - " name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:cfgvalue(section) == self.enabled then %> checked="checked"<% end %> value="1" /> -<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/view/cbi/header.htm b/core/src/view/cbi/header.htm deleted file mode 100644 index 4229aaf0d..000000000 --- a/core/src/view/cbi/header.htm +++ /dev/null @@ -1,7 +0,0 @@ -<%+header%> -
-
- - - -
diff --git a/core/src/view/cbi/lvalue.htm b/core/src/view/cbi/lvalue.htm deleted file mode 100644 index f1ae5a093..000000000 --- a/core/src/view/cbi/lvalue.htm +++ /dev/null @@ -1,16 +0,0 @@ -<%+cbi/valueheader%> -<% if self.widget == "select" then %> - -<% elseif self.widget == "radio" then - local c = 0; - for i, key in pairs(self.keylist) do - c = c + 1%> - <%=self.vallist[i]%>"<% if self:cfgvalue(section) == key then %> checked="checked"<% end %> value="<%=key%>" /> -<% if c == self.size then c = 0 %>
-<% end end %> -<% end %> -<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/view/cbi/map.htm b/core/src/view/cbi/map.htm deleted file mode 100644 index 835393c1c..000000000 --- a/core/src/view/cbi/map.htm +++ /dev/null @@ -1,6 +0,0 @@ -
-

<%=self.title%>

-
<%=self.description%>
-<% self:render_children() %> -
-
diff --git a/core/src/view/cbi/mvalue.htm b/core/src/view/cbi/mvalue.htm deleted file mode 100644 index bed66e569..000000000 --- a/core/src/view/cbi/mvalue.htm +++ /dev/null @@ -1,19 +0,0 @@ -<% -local v = self:valuelist(section) -%> -<%+cbi/valueheader%> -<% if self.widget == "select" then %> - -<% elseif self.widget == "checkbox" then - local c = 0; - for i, key in pairs(self.keylist) do - c = c + 1%> - <%=self.vallist[i]%>[]"<% if luci.util.contains(v, key) then %> checked="checked"<% end %> value="<%=key%>" /> -<% if c == self.size then c = 0 %>
-<% end end %> -<% end %> -<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/view/cbi/nsection.htm b/core/src/view/cbi/nsection.htm deleted file mode 100644 index fff597ad0..000000000 --- a/core/src/view/cbi/nsection.htm +++ /dev/null @@ -1,20 +0,0 @@ -<% if self:cfgvalue(self.section) then -section = self.section %> -
-

<%=self.title%>

-
<%=self.description%>
- <% if self.addremove then %>
- -
<% end %> -
-<%+cbi/ucisection%> -
-
-
-<% elseif self.addremove then %> -
-

<%=self.title%>

-
<%=self.description%>
- -
-<% end %> diff --git a/core/src/view/cbi/tblsection.htm b/core/src/view/cbi/tblsection.htm deleted file mode 100644 index df16efbed..000000000 --- a/core/src/view/cbi/tblsection.htm +++ /dev/null @@ -1,39 +0,0 @@ -
-

<%=self.title%>

-
<%=self.description%>
-
-
-<% for i, k in pairs(self.children) do %> -
<%=k.title%>
-<% end %> -
-
-<% for i, k in pairs(self.children) do %> -
<%=k.description%>
-<% end %> -
-<% for i, k in ipairs(self:cfgsections()) do%> - <% if not self.anonymous then %>

<%=k%>

<% end %> -<% -section = k -scope = {valueheader = "cbi/tiny_valueheader", valuefooter = "cbi/tiny_valuefooter"} -%> -
-<%+cbi/ucisection%> - <% if self.addremove then %>
- -
<% end %> -
-<% end %> -<% if self.addremove then %> -
- <% if self.anonymous then %> - - <% else %> - - - <% end %><% if self.err_invalid then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> -
-
-<% end %> -
diff --git a/core/src/view/cbi/tiny_valuefooter.htm b/core/src/view/cbi/tiny_valuefooter.htm deleted file mode 100644 index e65ebb6c0..000000000 --- a/core/src/view/cbi/tiny_valuefooter.htm +++ /dev/null @@ -1,6 +0,0 @@ - <% if self.tag_invalid[section] then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> -
- <% if #self.deps > 0 then %><% end %> \ No newline at end of file diff --git a/core/src/view/cbi/tiny_valueheader.htm b/core/src/view/cbi/tiny_valueheader.htm deleted file mode 100644 index b9b26bd6a..000000000 --- a/core/src/view/cbi/tiny_valueheader.htm +++ /dev/null @@ -1 +0,0 @@ -
"> diff --git a/core/src/view/cbi/tsection.htm b/core/src/view/cbi/tsection.htm deleted file mode 100644 index 37b18b5d4..000000000 --- a/core/src/view/cbi/tsection.htm +++ /dev/null @@ -1,25 +0,0 @@ -
-

<%=self.title%>

-
<%=self.description%>
-<% for i, k in ipairs(self:cfgsections()) do%> - <% if self.addremove then %>
- -
<% end %> - <% if not self.anonymous then %>

<%=k%>

<% end %> -<% section = k %> -
-<%+cbi/ucisection%> -
-
-<% end %> -<% if self.addremove then %> -
- <% if self.anonymous then %> - - <% else %> - - - <% end %><% if self.err_invalid then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> -
-<% end %> -
diff --git a/core/src/view/cbi/ucisection.htm b/core/src/view/cbi/ucisection.htm deleted file mode 100644 index 0abc37e7c..000000000 --- a/core/src/view/cbi/ucisection.htm +++ /dev/null @@ -1,20 +0,0 @@ -<% self:render_children(section, scope or {}) %> - <% if #self.optionals[section] > 0 or self.dynamic then %> -
- <% if self.dynamic then %> - - <% else %> - - - <% end %> - -
- <% end %> \ No newline at end of file diff --git a/core/src/view/cbi/value.htm b/core/src/view/cbi/value.htm deleted file mode 100644 index 31bf38f77..000000000 --- a/core/src/view/cbi/value.htm +++ /dev/null @@ -1,3 +0,0 @@ -<%+cbi/valueheader%> - size="<%=self.size%>" <% end %><% if self.maxlength then %>maxlength="<%=self.maxlength%>" <% end %>name="cbid.<%=self.config.."."..section.."."..self.option%>" id="cbid.<%=self.config.."."..section.."."..self.option%>" value="<%=self:cfgvalue(section)%>" /> -<%+cbi/valuefooter%> diff --git a/core/src/view/cbi/valuefooter.htm b/core/src/view/cbi/valuefooter.htm deleted file mode 100644 index bc9d1b127..000000000 --- a/core/src/view/cbi/valuefooter.htm +++ /dev/null @@ -1,5 +0,0 @@ -<% if valuefooter then - include(valuefooter) -else - include("cbi/full_valuefooter") -end %> \ No newline at end of file diff --git a/core/src/view/cbi/valueheader.htm b/core/src/view/cbi/valueheader.htm deleted file mode 100644 index 8d9802f57..000000000 --- a/core/src/view/cbi/valueheader.htm +++ /dev/null @@ -1,5 +0,0 @@ -<% if valueheader then - include(valueheader) -else - include("cbi/full_valueheader") -end %> \ No newline at end of file diff --git a/core/src/view/error404.htm b/core/src/view/error404.htm deleted file mode 100644 index 60daee2cb..000000000 --- a/core/src/view/error404.htm +++ /dev/null @@ -1,5 +0,0 @@ -<%+header%> -

404 Not Found

-

Sorry, the object you requested was not found.

-Unable to dispatch: <%=luci.http.env.PATH_INFO%> -<%+footer%> \ No newline at end of file diff --git a/core/src/view/error500.htm b/core/src/view/error500.htm deleted file mode 100644 index 8af22e8f2..000000000 --- a/core/src/view/error500.htm +++ /dev/null @@ -1,5 +0,0 @@ -<%+header%> -

500 Internal Server Error

-

Sorry, the server encountered an unexpected error.

-<%=message%> -<%+footer%> \ No newline at end of file diff --git a/core/src/view/footer.htm b/core/src/view/footer.htm deleted file mode 100644 index c8506ac5c..000000000 --- a/core/src/view/footer.htm +++ /dev/null @@ -1,7 +0,0 @@ -
-
-
- - - - \ No newline at end of file diff --git a/core/src/view/header.htm b/core/src/view/header.htm deleted file mode 100644 index 5f876781f..000000000 --- a/core/src/view/header.htm +++ /dev/null @@ -1,137 +0,0 @@ -<% -require("luci.sys") -local load1, load5, load15 = luci.sys.loadavg() - -local request = require("luci.dispatcher").request -local category = request[1] -local tree = luci.dispatcher.node() -local cattree = category and luci.dispatcher.node(category) -local node = luci.dispatcher.dispatched - -local c = tree -for i,r in ipairs(request) do - if c.nodes and c.nodes[r] then - c = c.nodes[r] - c._menu_selected = true - end -end - -require("luci.i18n").loadc("default") - -require("luci.http").prepare_content("text/html") -%> - - - - - <% if node and node.css then %><% end %> - - - LuCI - Lua Configuration Interface - - - - -
-<%:path Pfad%>: <% -local c = tree -local url = controller -for k,v in pairs(request) do - if c.nodes and c.nodes[v] then - c = c.nodes[v] - url = url .. "/" .. v - %><%=c.title or v%> <% if k ~= #request then %>» <% end - end -end -%> -
- -
- -<% end - end -end -%> -
- -
\ No newline at end of file diff --git a/i18n/english/Makefile b/i18n/english/Makefile new file mode 100644 index 000000000..81a96f6a8 --- /dev/null +++ b/i18n/english/Makefile @@ -0,0 +1,2 @@ +include ../../build/config.mk +include ../../build/module.mk \ No newline at end of file diff --git a/i18n/english/src/i18n/cbi.en b/i18n/english/src/i18n/cbi.en new file mode 100644 index 000000000..6069cec45 --- /dev/null +++ b/i18n/english/src/i18n/cbi.en @@ -0,0 +1,4 @@ +cbi_add = "Add entry" +cbi_del = "Remove entry" +cbi_invalid = "Error: Invalid input value" +cbi_addopt = "-- Field --" \ No newline at end of file diff --git a/web/Makefile b/web/Makefile new file mode 100644 index 000000000..5e64bcd1f --- /dev/null +++ b/web/Makefile @@ -0,0 +1,2 @@ +include ../build/config.mk +include ../build/module.mk \ No newline at end of file diff --git a/web/root/etc/config/luci b/web/root/etc/config/luci new file mode 100644 index 000000000..87170a9b1 --- /dev/null +++ b/web/root/etc/config/luci @@ -0,0 +1,39 @@ +config core main + option lang de + option mediaurlbase /luci/fledermaus + option resourcebase /luci/resources + +config core brand + option title "OpenWRT Kamikaze" + option subtitle Freifunkedition + option firmware "OpenWRT Kamikaze" + option distro "Freifunk Firmware 2.x" + +config extern flash_keep + option uci "/etc/config" + option dropbear "/etc/dropbear" + option openvpn "/etc/openvpn" + option passwd "/etc/passwd" + option ipkg "/etc/ipkg.conf" + option httpd "/etc/httpd.conf" + option firewall "/etc/firewall.user" + +config event uci_oncommit + option network "/etc/init.d/network restart" + option wireless "/etc/init.d/network restart" + option olsr "/sbin/reboot" + option dhcp "/etc/init.d/dnsmasq restart" + option luci_fw "/etc/init.d/luci_fw restart" + option dropbear "/etc/init.d/dropbear restart" + option httpd "/etc/init.d/httpd restart" + option fstab "/etc/init.d/fstab restart" + option qos "/etc/init.d/qos restart" + option luci_splash "/etc/init.d/luci_splash restart" + option freifunk "/etc/init.d/luci_freifunk restart" + +config internal languages + option de "Deutsch" + option en "English" + +config internal themes + option Fledermaus "/luci/fledermaus" diff --git a/web/root/www/resources/cbi.js b/web/root/www/resources/cbi.js new file mode 100644 index 000000000..a3a47aa45 --- /dev/null +++ b/web/root/www/resources/cbi.js @@ -0,0 +1,42 @@ +var cbi_d = {}; + +function cbi_d_add(field, target, value) { + if (!cbi_d[target]) { + cbi_d[target] = {}; + } + if (!cbi_d[target][value]) { + cbi_d[target][value] = []; + } + cbi_d[target][value].push(field); +} + +function cbi_d_update(target) { + if (!cbi_d[target]) { + return; + } + + for (var x in cbi_d[target]) { + for (var i=0; i + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- +module("luci.cbi", package.seeall) + +require("luci.template") +require("luci.util") +require("luci.http") +require("luci.model.uci") + +local class = luci.util.class +local instanceof = luci.util.instanceof + +-- Loads a CBI map from given file, creating an environment and returns it +function load(cbimap) + require("luci.fs") + require("luci.i18n") + require("luci.config") + require("luci.sys") + + local cbidir = luci.sys.libpath() .. "/model/cbi/" + local func, err = loadfile(cbidir..cbimap..".lua") + + if not func then + return nil + end + + luci.i18n.loadc("cbi") + + luci.util.resfenv(func) + luci.util.updfenv(func, luci.cbi) + luci.util.extfenv(func, "translate", luci.i18n.translate) + + local map = func() + + if not instanceof(map, Map) then + error("CBI map returns no valid map object!") + return nil + end + + return map +end + +-- Node pseudo abstract class +Node = class() + +function Node.__init__(self, title, description) + self.children = {} + self.title = title or "" + self.description = description or "" + self.template = "cbi/node" +end + +-- Append child nodes +function Node.append(self, obj) + table.insert(self.children, obj) +end + +-- Parse this node and its children +function Node.parse(self, ...) + for k, child in ipairs(self.children) do + child:parse(...) + end +end + +-- Render this node +function Node.render(self, scope) + scope = scope or {} + scope.self = self + + luci.template.render(self.template, scope) +end + +-- Render the children +function Node.render_children(self, ...) + for k, node in ipairs(self.children) do + node:render(...) + end +end + + +--[[ +A simple template element +]]-- +Template = class(Node) + +function Template.__init__(self, template) + Node.__init__(self) + self.template = template +end + + +--[[ +Map - A map describing a configuration file +]]-- +Map = class(Node) + +function Map.__init__(self, config, ...) + Node.__init__(self, ...) + self.config = config + self.template = "cbi/map" + self.uci = luci.model.uci.Session() + self.ucidata, self.uciorder = self.uci:sections(self.config) + if not self.ucidata or not self.uciorder then + error("Unable to read UCI data: " .. self.config) + end +end + +-- Use optimized UCI writing +function Map.parse(self, ...) + self.uci:t_load(self.config) + Node.parse(self, ...) + self.uci:t_save(self.config) +end + +-- Creates a child section +function Map.section(self, class, ...) + if instanceof(class, AbstractSection) then + local obj = class(self, ...) + self:append(obj) + return obj + else + error("class must be a descendent of AbstractSection") + end +end + +-- UCI add +function Map.add(self, sectiontype) + local name = self.uci:t_add(self.config, sectiontype) + if name then + self.ucidata[name] = {} + self.ucidata[name][".type"] = sectiontype + table.insert(self.uciorder, name) + end + return name +end + +-- UCI set +function Map.set(self, section, option, value) + local stat = self.uci:t_set(self.config, section, option, value) + if stat then + local val = self.uci:t_get(self.config, section, option) + if option then + self.ucidata[section][option] = val + else + if not self.ucidata[section] then + self.ucidata[section] = {} + end + self.ucidata[section][".type"] = val + table.insert(self.uciorder, section) + end + end + return stat +end + +-- UCI del +function Map.del(self, section, option) + local stat = self.uci:t_del(self.config, section, option) + if stat then + if option then + self.ucidata[section][option] = nil + else + self.ucidata[section] = nil + for i, k in ipairs(self.uciorder) do + if section == k then + table.remove(self.uciorder, i) + end + end + end + end + return stat +end + +-- UCI get (cached) +function Map.get(self, section, option) + if not section then + return self.ucidata, self.uciorder + elseif option and self.ucidata[section] then + return self.ucidata[section][option] + else + return self.ucidata[section] + end +end + + +--[[ +AbstractSection +]]-- +AbstractSection = class(Node) + +function AbstractSection.__init__(self, map, sectiontype, ...) + Node.__init__(self, ...) + self.sectiontype = sectiontype + self.map = map + self.config = map.config + self.optionals = {} + + self.optional = true + self.addremove = false + self.dynamic = false +end + +-- Appends a new option +function AbstractSection.option(self, class, ...) + if instanceof(class, AbstractValue) then + local obj = class(self.map, ...) + self:append(obj) + return obj + else + error("class must be a descendent of AbstractValue") + end +end + +-- Parse optional options +function AbstractSection.parse_optionals(self, section) + if not self.optional then + return + end + + self.optionals[section] = {} + + local field = luci.http.formvalue("cbi.opt."..self.config.."."..section) + for k,v in ipairs(self.children) do + if v.optional and not v:cfgvalue(section) then + if field == v.option then + field = nil + else + table.insert(self.optionals[section], v) + end + end + end + + if field and #field > 0 and self.dynamic then + self:add_dynamic(field) + end +end + +-- Add a dynamic option +function AbstractSection.add_dynamic(self, field, optional) + local o = self:option(Value, field, field) + o.optional = optional +end + +-- Parse all dynamic options +function AbstractSection.parse_dynamic(self, section) + if not self.dynamic then + return + end + + local arr = luci.util.clone(self:cfgvalue(section)) + local form = luci.http.formvaluetable("cbid."..self.config.."."..section) + for k, v in pairs(form) do + arr[k] = v + end + + for key,val in pairs(arr) do + local create = true + + for i,c in ipairs(self.children) do + if c.option == key then + create = false + end + end + + if create and key:sub(1, 1) ~= "." then + self:add_dynamic(key, true) + end + end +end + +-- Returns the section's UCI table +function AbstractSection.cfgvalue(self, section) + return self.map:get(section) +end + +-- Removes the section +function AbstractSection.remove(self, section) + return self.map:del(section) +end + +-- Creates the section +function AbstractSection.create(self, section) + return self.map:set(section, nil, self.sectiontype) +end + + + +--[[ +NamedSection - A fixed configuration section defined by its name +]]-- +NamedSection = class(AbstractSection) + +function NamedSection.__init__(self, map, section, ...) + AbstractSection.__init__(self, map, ...) + self.template = "cbi/nsection" + + self.section = section + self.addremove = false +end + +function NamedSection.parse(self) + local s = self.section + local active = self:cfgvalue(s) + + + if self.addremove then + local path = self.config.."."..s + if active then -- Remove the section + if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then + return + end + else -- Create and apply default values + if luci.http.formvalue("cbi.cns."..path) and self:create(s) then + for k,v in pairs(self.children) do + v:write(s, v.default) + end + end + end + end + + if active then + AbstractSection.parse_dynamic(self, s) + if luci.http.formvalue("cbi.submit") then + Node.parse(self, s) + end + AbstractSection.parse_optionals(self, s) + end +end + + +--[[ +TypedSection - A (set of) configuration section(s) defined by the type + addremove: Defines whether the user can add/remove sections of this type + anonymous: Allow creating anonymous sections + validate: a validation function returning nil if the section is invalid +]]-- +TypedSection = class(AbstractSection) + +function TypedSection.__init__(self, ...) + AbstractSection.__init__(self, ...) + self.template = "cbi/tsection" + self.deps = {} + self.excludes = {} + + self.anonymous = false +end + +-- Return all matching UCI sections for this TypedSection +function TypedSection.cfgsections(self) + local sections = {} + local map, order = self.map:get() + + for i, k in ipairs(order) do + if map[k][".type"] == self.sectiontype then + if self:checkscope(k) then + table.insert(sections, k) + end + end + end + + return sections +end + +-- Creates a new section of this type with the given name (or anonymous) +function TypedSection.create(self, name) + if name then + self.map:set(name, nil, self.sectiontype) + else + name = self.map:add(self.sectiontype) + end + + for k,v in pairs(self.children) do + if v.default then + self.map:set(name, v.option, v.default) + end + end +end + +-- Limits scope to sections that have certain option => value pairs +function TypedSection.depends(self, option, value) + table.insert(self.deps, {option=option, value=value}) +end + +-- Excludes several sections by name +function TypedSection.exclude(self, field) + self.excludes[field] = true +end + +function TypedSection.parse(self) + if self.addremove then + -- Create + local crval = "cbi.cts." .. self.config .. "." .. self.sectiontype + local name = luci.http.formvalue(crval) + if self.anonymous then + if name then + self:create() + end + else + if name then + -- Ignore if it already exists + if self:cfgvalue(name) then + name = nil; + end + + name = self:checkscope(name) + + if not name then + self.err_invalid = true + end + + if name and name:len() > 0 then + self:create(name) + end + end + end + + -- Remove + crval = "cbi.rts." .. self.config + name = luci.http.formvaluetable(crval) + for k,v in pairs(name) do + if self:cfgvalue(k) and self:checkscope(k) then + self:remove(k) + end + end + end + + for i, k in ipairs(self:cfgsections()) do + AbstractSection.parse_dynamic(self, k) + if luci.http.formvalue("cbi.submit") then + Node.parse(self, k) + end + AbstractSection.parse_optionals(self, k) + end +end + +-- Verifies scope of sections +function TypedSection.checkscope(self, section) + -- Check if we are not excluded + if self.excludes[section] then + return nil + end + + -- Check if at least one dependency is met + if #self.deps > 0 and self:cfgvalue(section) then + local stat = false + + for k, v in ipairs(self.deps) do + if self:cfgvalue(section)[v.option] == v.value then + stat = true + end + end + + if not stat then + return nil + end + end + + return self:validate(section) +end + + +-- Dummy validate function +function TypedSection.validate(self, section) + return section +end + + +--[[ +AbstractValue - An abstract Value Type + null: Value can be empty + valid: A function returning the value if it is valid otherwise nil + depends: A table of option => value pairs of which one must be true + default: The default value + size: The size of the input fields + rmempty: Unset value if empty + optional: This value is optional (see AbstractSection.optionals) +]]-- +AbstractValue = class(Node) + +function AbstractValue.__init__(self, map, option, ...) + Node.__init__(self, ...) + self.option = option + self.map = map + self.config = map.config + self.tag_invalid = {} + self.deps = {} + + self.rmempty = false + self.default = nil + self.size = nil + self.optional = false +end + +-- Add a dependencie to another section field +function AbstractValue.depends(self, field, value) + table.insert(self.deps, {field=field, value=value}) +end + +-- Return whether this object should be created +function AbstractValue.formcreated(self, section) + local key = "cbi.opt."..self.config.."."..section + return (luci.http.formvalue(key) == self.option) +end + +-- Returns the formvalue for this object +function AbstractValue.formvalue(self, section) + local key = "cbid."..self.map.config.."."..section.."."..self.option + return luci.http.formvalue(key) +end + +function AbstractValue.parse(self, section) + local fvalue = self:formvalue(section) + + if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI + fvalue = self:validate(fvalue) + if not fvalue then + self.tag_invalid[section] = true + end + if fvalue and not (fvalue == self:cfgvalue(section)) then + self:write(section, fvalue) + end + else -- Unset the UCI or error + if self.rmempty or self.optional then + self:remove(section) + end + end +end + +-- Render if this value exists or if it is mandatory +function AbstractValue.render(self, s, scope) + if not self.optional or self:cfgvalue(s) or self:formcreated(s) then + scope = scope or {} + scope.section = s + Node.render(self, scope) + end +end + +-- Return the UCI value of this object +function AbstractValue.cfgvalue(self, section) + return self.map:get(section, self.option) +end + +-- Validate the form value +function AbstractValue.validate(self, value) + return value +end + +-- Write to UCI +function AbstractValue.write(self, section, value) + return self.map:set(section, self.option, value) +end + +-- Remove from UCI +function AbstractValue.remove(self, section) + return self.map:del(section, self.option) +end + + + + +--[[ +Value - A one-line value + maxlength: The maximum length + isnumber: The value must be a valid (floating point) number + isinteger: The value must be a valid integer + ispositive: The value must be positive (and a number) +]]-- +Value = class(AbstractValue) + +function Value.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/value" + + self.maxlength = nil + self.isnumber = false + self.isinteger = false +end + +-- This validation is a bit more complex +function Value.validate(self, val) + if self.maxlength and tostring(val):len() > self.maxlength then + val = nil + end + + return luci.util.validate(val, self.isnumber, self.isinteger) +end + + +-- DummyValue - This does nothing except being there +DummyValue = class(AbstractValue) + +function DummyValue.__init__(self, map, ...) + AbstractValue.__init__(self, map, ...) + self.template = "cbi/dvalue" + self.value = nil +end + +function DummyValue.parse(self) + +end + +function DummyValue.render(self, s) + luci.template.render(self.template, {self=self, section=s}) +end + + +--[[ +Flag - A flag being enabled or disabled +]]-- +Flag = class(AbstractValue) + +function Flag.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/fvalue" + + self.enabled = "1" + self.disabled = "0" +end + +-- A flag can only have two states: set or unset +function Flag.parse(self, section) + local fvalue = self:formvalue(section) + + if fvalue then + fvalue = self.enabled + else + fvalue = self.disabled + end + + if fvalue == self.enabled or (not self.optional and not self.rmempty) then + if not(fvalue == self:cfgvalue(section)) then + self:write(section, fvalue) + end + else + self:remove(section) + end +end + + + +--[[ +ListValue - A one-line value predefined in a list + widget: The widget that will be used (select, radio) +]]-- +ListValue = class(AbstractValue) + +function ListValue.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/lvalue" + self.keylist = {} + self.vallist = {} + + self.size = 1 + self.widget = "select" +end + +function ListValue.value(self, key, val) + val = val or key + table.insert(self.keylist, tostring(key)) + table.insert(self.vallist, tostring(val)) +end + +function ListValue.validate(self, val) + if luci.util.contains(self.keylist, val) then + return val + else + return nil + end +end + + + +--[[ +MultiValue - Multiple delimited values + widget: The widget that will be used (select, checkbox) + delimiter: The delimiter that will separate the values (default: " ") +]]-- +MultiValue = class(AbstractValue) + +function MultiValue.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/mvalue" + self.keylist = {} + self.vallist = {} + + self.widget = "checkbox" + self.delimiter = " " +end + +function MultiValue.value(self, key, val) + val = val or key + table.insert(self.keylist, tostring(key)) + table.insert(self.vallist, tostring(val)) +end + +function MultiValue.valuelist(self, section) + local val = self:cfgvalue(section) + + if not(type(val) == "string") then + return {} + end + + return luci.util.split(val, self.delimiter) +end + +function MultiValue.validate(self, val) + if not(type(val) == "string") then + return nil + end + + local result = "" + + for value in val:gmatch("[^\n]+") do + if luci.util.contains(self.keylist, value) then + result = result .. self.delimiter .. value + end + end + + if result:len() > 0 then + return result:sub(self.delimiter:len() + 1) + else + return nil + end +end \ No newline at end of file diff --git a/web/src/config.lua b/web/src/config.lua new file mode 100644 index 000000000..854b12814 --- /dev/null +++ b/web/src/config.lua @@ -0,0 +1,48 @@ +--[[ +LuCI - Configuration + +Description: +Some LuCI configuration values read from uci file "luci" + + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- + +module("luci.config", package.seeall) +require("luci.model.uci") +require("luci.util") +require("luci.sys") + +-- Warning! This is only for fallback and compatibility purporses! -- +main = {} + +-- This is where stylesheets and images go +main.mediaurlbase = "/luci/media" + +-- Does anybody think about browser autodetect here? +-- Too bad busybox doesn't populate HTTP_ACCEPT_LANGUAGE +main.lang = "de" + + +-- Now overwrite with UCI values +local ucidata = luci.model.uci.sections("luci") +if ucidata then + luci.util.update(luci.config, ucidata) +end \ No newline at end of file diff --git a/web/src/dispatcher.lua b/web/src/dispatcher.lua new file mode 100644 index 000000000..175f0dcb0 --- /dev/null +++ b/web/src/dispatcher.lua @@ -0,0 +1,281 @@ +--[[ +LuCI - Dispatcher + +Description: +The request dispatcher and module dispatcher generators + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- +module("luci.dispatcher", package.seeall) +require("luci.http") +require("luci.sys") +require("luci.fs") + +-- Local dispatch database +local tree = {nodes={}} + +-- Index table +local index = {} + +-- Global request object +request = {} + +-- Active dispatched node +dispatched = nil + +-- Status fields +built_index = false +built_tree = false + + +-- Builds a URL +function build_url(...) + return luci.http.dispatcher() .. "/" .. table.concat(arg, "/") +end + +-- Sends a 404 error code and renders the "error404" template if available +function error404(message) + luci.http.status(404, "Not Found") + message = message or "Not Found" + + require("luci.template") + if not pcall(luci.template.render, "error404") then + luci.http.prepare_content("text/plain") + print(message) + end + return false +end + +-- Sends a 500 error code and renders the "error500" template if available +function error500(message) + luci.http.status(500, "Internal Server Error") + + require("luci.template") + if not pcall(luci.template.render, "error500", {message=message}) then + luci.http.prepare_content("text/plain") + print(message) + end + return false +end + +-- Creates a request object for dispatching +function httpdispatch() + local pathinfo = luci.http.env.PATH_INFO or "" + local c = tree + + for s in pathinfo:gmatch("([%w_]+)") do + table.insert(request, s) + end + + dispatch() +end + +-- Dispatches a request +function dispatch() + if not built_tree then + createtree() + end + + local c = tree + local track = {} + + for i, s in ipairs(request) do + c = c.nodes[s] + if not c then + break + end + + for k, v in pairs(c) do + track[k] = v + end + end + + + if track.i18n then + require("luci.i18n").loadc(track.i18n) + end + + if track.setgroup then + luci.sys.process.setgroup(track.setgroup) + end + + if track.setuser then + luci.sys.process.setuser(track.setuser) + end + + -- Init template engine + local tpl = require("luci.template") + tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end + tpl.viewns.controller = luci.http.dispatcher() + tpl.viewns.uploadctrl = luci.http.dispatcher_upload() + tpl.viewns.media = luci.config.main.mediaurlbase + tpl.viewns.resource = luci.config.main.resourcebase + + -- Load default translation + require("luci.i18n").loadc("default") + + + if c and type(c.target) == "function" then + dispatched = c + + stat, err = pcall(c.target) + if not stat then + error500(err) + end + else + error404() + end +end + +-- Generates the dispatching tree +function createindex() + index = {} + local path = luci.sys.libpath() .. "/controller/" + local suff = ".lua" + + if pcall(require, "fastindex") then + createindex_fastindex(path, suff) + else + createindex_plain(path, suff) + end + + built_index = true +end + +-- Uses fastindex to create the dispatching tree +function createindex_fastindex(path, suffix) + local fi = fastindex.new("index") + fi.add(path .. "*" .. suffix) + fi.add(path .. "*/*" .. suffix) + fi.scan() + + for k, v in pairs(fi.indexes) do + index[v[2]] = v[1] + end +end + +-- Calls the index function of all available controllers +function createindex_plain(path, suffix) + local controllers = luci.util.combine( + luci.fs.glob(path .. "*" .. suffix) or {}, + luci.fs.glob(path .. "*/*" .. suffix) or {} + ) + + for i,c in ipairs(controllers) do + c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".") + stat, mod = pcall(require, c) + + if stat and mod and type(mod.index) == "function" then + index[c] = mod.index + end + end +end + +-- Creates the dispatching tree from the index +function createtree() + if not built_index then + createindex() + end + + for k, v in pairs(index) do + luci.util.updfenv(v, _M) + + local stat, mod = pcall(require, k) + if stat then + luci.util.updfenv(v, mod) + end + + pcall(v) + end + + built_tree = true +end + +-- Shortcut for creating a dispatching node +function entry(path, target, title, order, add) + add = add or {} + + local c = node(path) + c.target = target + c.title = title + c.order = order + + for k,v in pairs(add) do + c[k] = v + end + + return c +end + +-- Fetch a dispatching node +function node(...) + local c = tree + + if arg[1] and type(arg[1]) == "table" then + arg = arg[1] + end + + for k,v in ipairs(arg) do + if not c.nodes[v] then + c.nodes[v] = {nodes={}} + end + + c = c.nodes[v] + end + + return c +end + +-- Subdispatchers -- +function alias(...) + local req = arg + return function() + request = req + dispatch() + end +end + +function template(name) + require("luci.template") + return function() luci.template.render(name) end +end + +function cbi(model) + require("luci.cbi") + require("luci.template") + + return function() + local stat, res = pcall(luci.cbi.load, model) + if not stat then + error500(res) + return true + end + + local stat, err = pcall(res.parse, res) + if not stat then + error500(err) + return true + end + + luci.template.render("cbi/header") + res:render() + luci.template.render("cbi/footer") + end +end diff --git a/web/src/http.lua b/web/src/http.lua new file mode 100644 index 000000000..fa8821c5a --- /dev/null +++ b/web/src/http.lua @@ -0,0 +1,36 @@ +--[[ +LuCI - HTTP-Interaction + +Description: +HTTP-Header manipulator and form variable preprocessor + +FileId: +$Id$ + +ToDo: +- Cookie handling + +License: +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- + +module("luci.http", package.seeall) + +if ENV and ENV.HASERLVER then + require("luci.sgi.haserl") +elseif webuci then + require("luci.sgi.webuci") +end \ No newline at end of file diff --git a/web/src/i18n.lua b/web/src/i18n.lua new file mode 100644 index 000000000..3a8a9a6c7 --- /dev/null +++ b/web/src/i18n.lua @@ -0,0 +1,63 @@ +--[[ +LuCI - Internationalisation + +Description: +A very minimalistic but yet effective internationalisation module + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- + +module("luci.i18n", package.seeall) +require("luci.sys") + +table = {} +i18ndir = luci.sys.libpath() .. "/i18n/" + +-- Clears the translation table +function clear() + table = {} +end + +-- Loads a translation and copies its data into the global translation table +function load(file) + local f = loadfile(i18ndir .. file) + if f then + setfenv(f, table) + f() + return true + else + return false + end +end + +-- Same as load but autocompletes the filename with .LANG from config.lang +function loadc(file) + return load(file .. "." .. require("luci.config").main.lang) +end + +-- Returns the i18n-value defined by "key" or if there is no such: "default" +function translate(key, default) + return table[key] or default +end + +-- Translate shourtcut with sprintf/string.format inclusion +function translatef(key, default, ...) + return translate(key, default):format(...) +end \ No newline at end of file diff --git a/web/src/template.lua b/web/src/template.lua new file mode 100644 index 000000000..369aa0a30 --- /dev/null +++ b/web/src/template.lua @@ -0,0 +1,220 @@ +--[[ +LuCI - Template Parser + +Description: +A template parser supporting includes, translations, Lua code blocks +and more. It can be used either as a compiler or as an interpreter. + +FileId: $Id$ + +License: +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- +module("luci.template", package.seeall) + +require("luci.config") +require("luci.util") +require("luci.fs") +require("luci.http") + +viewdir = luci.sys.libpath() .. "/view/" + + +-- Compile modes: +-- none: Never compile, only use precompiled data from files +-- memory: Always compile, do not save compiled files, ignore precompiled +-- file: Compile on demand, save compiled files, update precompiled +compiler_mode = "memory" + + +-- This applies to compiler modes "always" and "smart" +-- +-- Produce compiled lua code rather than lua sourcecode +-- WARNING: Increases template size heavily!!! +-- This produces the same bytecode as luac but does not have a strip option +compiler_enable_bytecode = false + + +-- Define the namespace for template modules +viewns = { + write = io.write, + include = function(name) Template(name):render(getfenv(2)) end, +} + +-- Compiles a given template into an executable Lua module +function compile(template) + -- Search all <% %> expressions (remember: Lua table indexes begin with #1) + local function expr_add(command) + table.insert(expr, command) + return "<%" .. tostring(#expr) .. "%>" + end + + -- As "expr" should be local, we have to assign it to the "expr_add" scope + local expr = {} + luci.util.extfenv(expr_add, "expr", expr) + + -- Save all expressiosn to table "expr" + template = template:gsub("<%%(.-)%%>", expr_add) + + local function sanitize(s) + s = luci.util.escape(s) + s = luci.util.escape(s, "'") + s = luci.util.escape(s, "\n") + return s + end + + -- Escape and sanitize all the template (all non-expressions) + template = sanitize(template) + + -- Template module header/footer declaration + local header = "write('" + local footer = "')" + + template = header .. template .. footer + + -- Replacements + local r_include = "')\ninclude('%s')\nwrite('" + local r_i18n = "'..translate('%1','%2')..'" + local r_pexec = "'..(%s or '')..'" + local r_exec = "')\n%s\nwrite('" + + -- Parse the expressions + for k,v in pairs(expr) do + local p = v:sub(1, 1) + local re = nil + if p == "+" then + re = r_include:format(sanitize(string.sub(v, 2))) + elseif p == ":" then + re = sanitize(v):gsub(":(.-) (.+)", r_i18n) + elseif p == "=" then + re = r_pexec:format(v:sub(2)) + else + re = r_exec:format(v) + end + template = template:gsub("<%%"..tostring(k).."%%>", re) + end + + if compiler_enable_bytecode then + tf = loadstring(template) + template = string.dump(tf) + end + + return template +end + +-- Oldstyle render shortcut +function render(name, scope, ...) + scope = scope or getfenv(2) + local s, t = pcall(Template, name) + if not s then + error(t) + else + t:render(scope, ...) + end +end + + +-- Template class +Template = luci.util.class() + +-- Shared template cache to store templates in to avoid unnecessary reloading +Template.cache = {} + + +-- Constructor - Reads and compiles the template on-demand +function Template.__init__(self, name) + if self.cache[name] then + self.template = self.cache[name] + else + self.template = nil + end + + -- Create a new namespace for this template + self.viewns = {} + + -- Copy over from general namespace + for k, v in pairs(viewns) do + self.viewns[k] = v + end + + -- If we have a cached template, skip compiling and loading + if self.template then + return + end + + -- Compile and build + local sourcefile = viewdir .. name .. ".htm" + local compiledfile = viewdir .. name .. ".lua" + local err + + if compiler_mode == "file" then + local tplmt = luci.fs.mtime(sourcefile) + local commt = luci.fs.mtime(compiledfile) + + -- Build if there is no compiled file or if compiled file is outdated + if ((commt == nil) and not (tplmt == nil)) + or (not (commt == nil) and not (tplmt == nil) and commt < tplmt) then + local source + source, err = luci.fs.readfile(sourcefile) + + if source then + local compiled = compile(source) + luci.fs.writefile(compiledfile, compiled) + self.template, err = loadstring(compiled) + end + else + self.template, err = loadfile(compiledfile) + end + + elseif compiler_mode == "none" then + self.template, err = loadfile(self.compiledfile) + + elseif compiler_mode == "memory" then + local source + source, err = luci.fs.readfile(sourcefile) + if source then + self.template, err = loadstring(compile(source)) + end + + end + + -- If we have no valid template throw error, otherwise cache the template + if not self.template then + error(err) + else + self.cache[name] = self.template + end +end + + +-- Renders a template +function Template.render(self, scope) + scope = scope or getfenv(2) + + -- Save old environment + local oldfenv = getfenv(self.template) + + -- Put our predefined objects in the scope of the template + luci.util.resfenv(self.template) + luci.util.updfenv(self.template, scope) + luci.util.updfenv(self.template, self.viewns) + + -- Now finally render the thing + self.template() + + -- Reset environment + setfenv(self.template, oldfenv) +end diff --git a/web/src/view/cbi/dvalue.htm b/web/src/view/cbi/dvalue.htm new file mode 100644 index 000000000..f54667def --- /dev/null +++ b/web/src/view/cbi/dvalue.htm @@ -0,0 +1,12 @@ +<%+cbi/valueheader%> +<% if self.value then + if type(self.value) == "function" then %> + <%=self:value(section)%> +<% else %> + <%=self.value%> +<% end +else %> + <%=self:cfgvalue(section)%> +<% end %> +  +<%+cbi/valuefooter%> diff --git a/web/src/view/cbi/footer.htm b/web/src/view/cbi/footer.htm new file mode 100644 index 000000000..2acf710cd --- /dev/null +++ b/web/src/view/cbi/footer.htm @@ -0,0 +1,7 @@ +
+ + + +
+ +<%+footer%> \ No newline at end of file diff --git a/web/src/view/cbi/full_valuefooter.htm b/web/src/view/cbi/full_valuefooter.htm new file mode 100644 index 000000000..6151a3a66 --- /dev/null +++ b/web/src/view/cbi/full_valuefooter.htm @@ -0,0 +1,8 @@ +
<%=self.description%> 
+
+ <% if self.tag_invalid[section] then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> +
+ <% if #self.deps > 0 then %><% end %> \ No newline at end of file diff --git a/web/src/view/cbi/full_valueheader.htm b/web/src/view/cbi/full_valueheader.htm new file mode 100644 index 000000000..062efa2dd --- /dev/null +++ b/web/src/view/cbi/full_valueheader.htm @@ -0,0 +1,3 @@ +
"> +
<%=self.title%>
+
\ No newline at end of file diff --git a/web/src/view/cbi/fvalue.htm b/web/src/view/cbi/fvalue.htm new file mode 100644 index 000000000..b609f1d4f --- /dev/null +++ b/web/src/view/cbi/fvalue.htm @@ -0,0 +1,3 @@ +<%+cbi/valueheader%> + " name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:cfgvalue(section) == self.enabled then %> checked="checked"<% end %> value="1" /> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/web/src/view/cbi/header.htm b/web/src/view/cbi/header.htm new file mode 100644 index 000000000..4229aaf0d --- /dev/null +++ b/web/src/view/cbi/header.htm @@ -0,0 +1,7 @@ +<%+header%> +
+
+ + + +
diff --git a/web/src/view/cbi/lvalue.htm b/web/src/view/cbi/lvalue.htm new file mode 100644 index 000000000..f1ae5a093 --- /dev/null +++ b/web/src/view/cbi/lvalue.htm @@ -0,0 +1,16 @@ +<%+cbi/valueheader%> +<% if self.widget == "select" then %> + +<% elseif self.widget == "radio" then + local c = 0; + for i, key in pairs(self.keylist) do + c = c + 1%> + <%=self.vallist[i]%>"<% if self:cfgvalue(section) == key then %> checked="checked"<% end %> value="<%=key%>" /> +<% if c == self.size then c = 0 %>
+<% end end %> +<% end %> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/web/src/view/cbi/map.htm b/web/src/view/cbi/map.htm new file mode 100644 index 000000000..835393c1c --- /dev/null +++ b/web/src/view/cbi/map.htm @@ -0,0 +1,6 @@ +
+

<%=self.title%>

+
<%=self.description%>
+<% self:render_children() %> +
+
diff --git a/web/src/view/cbi/mvalue.htm b/web/src/view/cbi/mvalue.htm new file mode 100644 index 000000000..bed66e569 --- /dev/null +++ b/web/src/view/cbi/mvalue.htm @@ -0,0 +1,19 @@ +<% +local v = self:valuelist(section) +%> +<%+cbi/valueheader%> +<% if self.widget == "select" then %> + +<% elseif self.widget == "checkbox" then + local c = 0; + for i, key in pairs(self.keylist) do + c = c + 1%> + <%=self.vallist[i]%>[]"<% if luci.util.contains(v, key) then %> checked="checked"<% end %> value="<%=key%>" /> +<% if c == self.size then c = 0 %>
+<% end end %> +<% end %> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/web/src/view/cbi/nsection.htm b/web/src/view/cbi/nsection.htm new file mode 100644 index 000000000..fff597ad0 --- /dev/null +++ b/web/src/view/cbi/nsection.htm @@ -0,0 +1,20 @@ +<% if self:cfgvalue(self.section) then +section = self.section %> +
+

<%=self.title%>

+
<%=self.description%>
+ <% if self.addremove then %>
+ +
<% end %> +
+<%+cbi/ucisection%> +
+
+
+<% elseif self.addremove then %> +
+

<%=self.title%>

+
<%=self.description%>
+ +
+<% end %> diff --git a/web/src/view/cbi/tblsection.htm b/web/src/view/cbi/tblsection.htm new file mode 100644 index 000000000..df16efbed --- /dev/null +++ b/web/src/view/cbi/tblsection.htm @@ -0,0 +1,39 @@ +
+

<%=self.title%>

+
<%=self.description%>
+
+
+<% for i, k in pairs(self.children) do %> +
<%=k.title%>
+<% end %> +
+
+<% for i, k in pairs(self.children) do %> +
<%=k.description%>
+<% end %> +
+<% for i, k in ipairs(self:cfgsections()) do%> + <% if not self.anonymous then %>

<%=k%>

<% end %> +<% +section = k +scope = {valueheader = "cbi/tiny_valueheader", valuefooter = "cbi/tiny_valuefooter"} +%> +
+<%+cbi/ucisection%> + <% if self.addremove then %>
+ +
<% end %> +
+<% end %> +<% if self.addremove then %> +
+ <% if self.anonymous then %> + + <% else %> + + + <% end %><% if self.err_invalid then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> +
+
+<% end %> +
diff --git a/web/src/view/cbi/tiny_valuefooter.htm b/web/src/view/cbi/tiny_valuefooter.htm new file mode 100644 index 000000000..e65ebb6c0 --- /dev/null +++ b/web/src/view/cbi/tiny_valuefooter.htm @@ -0,0 +1,6 @@ + <% if self.tag_invalid[section] then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> +
+ <% if #self.deps > 0 then %><% end %> \ No newline at end of file diff --git a/web/src/view/cbi/tiny_valueheader.htm b/web/src/view/cbi/tiny_valueheader.htm new file mode 100644 index 000000000..b9b26bd6a --- /dev/null +++ b/web/src/view/cbi/tiny_valueheader.htm @@ -0,0 +1 @@ +
"> diff --git a/web/src/view/cbi/tsection.htm b/web/src/view/cbi/tsection.htm new file mode 100644 index 000000000..37b18b5d4 --- /dev/null +++ b/web/src/view/cbi/tsection.htm @@ -0,0 +1,25 @@ +
+

<%=self.title%>

+
<%=self.description%>
+<% for i, k in ipairs(self:cfgsections()) do%> + <% if self.addremove then %>
+ +
<% end %> + <% if not self.anonymous then %>

<%=k%>

<% end %> +<% section = k %> +
+<%+cbi/ucisection%> +
+
+<% end %> +<% if self.addremove then %> +
+ <% if self.anonymous then %> + + <% else %> + + + <% end %><% if self.err_invalid then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> +
+<% end %> +
diff --git a/web/src/view/cbi/ucisection.htm b/web/src/view/cbi/ucisection.htm new file mode 100644 index 000000000..0abc37e7c --- /dev/null +++ b/web/src/view/cbi/ucisection.htm @@ -0,0 +1,20 @@ +<% self:render_children(section, scope or {}) %> + <% if #self.optionals[section] > 0 or self.dynamic then %> +
+ <% if self.dynamic then %> + + <% else %> + + + <% end %> + +
+ <% end %> \ No newline at end of file diff --git a/web/src/view/cbi/value.htm b/web/src/view/cbi/value.htm new file mode 100644 index 000000000..31bf38f77 --- /dev/null +++ b/web/src/view/cbi/value.htm @@ -0,0 +1,3 @@ +<%+cbi/valueheader%> + size="<%=self.size%>" <% end %><% if self.maxlength then %>maxlength="<%=self.maxlength%>" <% end %>name="cbid.<%=self.config.."."..section.."."..self.option%>" id="cbid.<%=self.config.."."..section.."."..self.option%>" value="<%=self:cfgvalue(section)%>" /> +<%+cbi/valuefooter%> diff --git a/web/src/view/cbi/valuefooter.htm b/web/src/view/cbi/valuefooter.htm new file mode 100644 index 000000000..bc9d1b127 --- /dev/null +++ b/web/src/view/cbi/valuefooter.htm @@ -0,0 +1,5 @@ +<% if valuefooter then + include(valuefooter) +else + include("cbi/full_valuefooter") +end %> \ No newline at end of file diff --git a/web/src/view/cbi/valueheader.htm b/web/src/view/cbi/valueheader.htm new file mode 100644 index 000000000..8d9802f57 --- /dev/null +++ b/web/src/view/cbi/valueheader.htm @@ -0,0 +1,5 @@ +<% if valueheader then + include(valueheader) +else + include("cbi/full_valueheader") +end %> \ No newline at end of file diff --git a/web/src/view/error404.htm b/web/src/view/error404.htm new file mode 100644 index 000000000..60daee2cb --- /dev/null +++ b/web/src/view/error404.htm @@ -0,0 +1,5 @@ +<%+header%> +

404 Not Found

+

Sorry, the object you requested was not found.

+Unable to dispatch: <%=luci.http.env.PATH_INFO%> +<%+footer%> \ No newline at end of file diff --git a/web/src/view/error500.htm b/web/src/view/error500.htm new file mode 100644 index 000000000..8af22e8f2 --- /dev/null +++ b/web/src/view/error500.htm @@ -0,0 +1,5 @@ +<%+header%> +

500 Internal Server Error

+

Sorry, the server encountered an unexpected error.

+<%=message%> +<%+footer%> \ No newline at end of file diff --git a/web/src/view/footer.htm b/web/src/view/footer.htm new file mode 100644 index 000000000..c8506ac5c --- /dev/null +++ b/web/src/view/footer.htm @@ -0,0 +1,7 @@ +
+
+
+ + + + \ No newline at end of file diff --git a/web/src/view/header.htm b/web/src/view/header.htm new file mode 100644 index 000000000..5f876781f --- /dev/null +++ b/web/src/view/header.htm @@ -0,0 +1,137 @@ +<% +require("luci.sys") +local load1, load5, load15 = luci.sys.loadavg() + +local request = require("luci.dispatcher").request +local category = request[1] +local tree = luci.dispatcher.node() +local cattree = category and luci.dispatcher.node(category) +local node = luci.dispatcher.dispatched + +local c = tree +for i,r in ipairs(request) do + if c.nodes and c.nodes[r] then + c = c.nodes[r] + c._menu_selected = true + end +end + +require("luci.i18n").loadc("default") + +require("luci.http").prepare_content("text/html") +%> + + + + + <% if node and node.css then %><% end %> + + + LuCI - Lua Configuration Interface + + + + +
+<%:path Pfad%>: <% +local c = tree +local url = controller +for k,v in pairs(request) do + if c.nodes and c.nodes[v] then + c = c.nodes[v] + url = url .. "/" .. v + %><%=c.title or v%> <% if k ~= #request then %>» <% end + end +end +%> +
+ +
+ +<% end + end +end +%> +
+ +
\ No newline at end of file