diff options
Diffstat (limited to 'libs/web/src')
-rw-r--r-- | libs/web/src/config.lua | 48 | ||||
-rw-r--r-- | libs/web/src/dispatcher.lua | 281 | ||||
-rw-r--r-- | libs/web/src/http.lua | 36 | ||||
-rw-r--r-- | libs/web/src/i18n.lua | 63 | ||||
-rw-r--r-- | libs/web/src/template.lua | 220 |
5 files changed, 648 insertions, 0 deletions
diff --git a/libs/web/src/config.lua b/libs/web/src/config.lua new file mode 100644 index 000000000..854b12814 --- /dev/null +++ b/libs/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 <steven@midlink.org> + +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/libs/web/src/dispatcher.lua b/libs/web/src/dispatcher.lua new file mode 100644 index 000000000..175f0dcb0 --- /dev/null +++ b/libs/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 <steven@midlink.org> + +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/libs/web/src/http.lua b/libs/web/src/http.lua new file mode 100644 index 000000000..fa8821c5a --- /dev/null +++ b/libs/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 <steven@midlink.org> + +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/libs/web/src/i18n.lua b/libs/web/src/i18n.lua new file mode 100644 index 000000000..3a8a9a6c7 --- /dev/null +++ b/libs/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 <steven@midlink.org> + +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/libs/web/src/template.lua b/libs/web/src/template.lua new file mode 100644 index 000000000..369aa0a30 --- /dev/null +++ b/libs/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 <steven@midlink.org> + +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 |