diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/root/etc/config/luci | 11 | ||||
-rw-r--r-- | core/src/dispatcher.lua | 309 | ||||
-rw-r--r-- | core/src/i18n/default.en | 8 | ||||
-rw-r--r-- | core/src/init.lua | 2 | ||||
-rw-r--r-- | core/src/menu.lua | 92 | ||||
-rw-r--r-- | core/src/template.lua | 2 | ||||
-rw-r--r-- | core/src/view/footer.htm | 2 | ||||
-rw-r--r-- | core/src/view/header.htm | 101 |
8 files changed, 188 insertions, 339 deletions
diff --git a/core/root/etc/config/luci b/core/root/etc/config/luci index b15b15289..7a7aa333e 100644 --- a/core/root/etc/config/luci +++ b/core/root/etc/config/luci @@ -1,10 +1,9 @@ config core main - option lang de - option mediaurlbase /ffluci/media - option imagebase /ffluci/images - -config core category_privileges - option public nobody:nogroup + option lang de + option mediaurlbase /ffluci/media + option resourcebase /ffluci/images + option title "Freifunk Kamikaze" + option subtitle Fledermausedition config extern flash_keep option uci "/etc/config" diff --git a/core/src/dispatcher.lua b/core/src/dispatcher.lua index 0a66ccd3a..84c665eda 100644 --- a/core/src/dispatcher.lua +++ b/core/src/dispatcher.lua @@ -4,63 +4,6 @@ FFLuCI - Dispatcher Description: The request dispatcher and module dispatcher generators - -The dispatching process: - For a detailed explanation of the dispatching process we assume: - You have installed the FFLuCI CGI-Dispatcher in /cgi-bin/ffluci - - To enforce a higher level of security only the CGI-Dispatcher - resides inside the web server's document root, everything else - stays inside an external directory, we assume this is /lua/ffluci - for this explanation. - - All controllers and action are reachable as sub-objects of /cgi-bin/ffluci - as if they were virtual folders and files - e.g.: /cgi-bin/ffluci/public/info/about - /cgi-bin/ffluci/admin/network/interfaces - and so on. - - The PATH_INFO variable holds the dispatch path and - will be split into three parts: /category/module/action - - Category: This is the category in which modules are stored in - By default there are two categories: - "public" - which is the default public category - "admin" - which is the default protected category - - As FFLuCI itself does not implement authentication - you should make sure that "admin" and other sensitive - categories are protected by the webserver. - - E.g. for busybox add a line like: - /cgi-bin/ffluci/admin:root:$p$root - to /etc/httpd.conf to protect the "admin" category - - - Module: This is the controller which will handle the request further - It is always a submodule of ffluci.controller, so a module - called "helloworld" will be stored in - /lua/ffluci/controller/helloworld.lua - You are free to submodule your controllers any further. - - Action: This is action that will be invoked after loading the module. - The kind of how the action will be dispatched depends on - the module dispatcher that is defined in the controller. - See the description of the default module dispatcher down - on this page for some examples. - - - The main dispatcher at first searches for the module by trying to - include ffluci.controller.category.module - (where "category" is the category name and "module" is the module name) - If this fails a 404 status code will be send to the client and FFLuCI exits - - Then the main dispatcher calls the module dispatcher - ffluci.controller.category.module.dispatcher with the request object - as the only argument. The module dispatcher is then responsible - for the further dispatching process. - - FileId: $Id$ @@ -80,48 +23,24 @@ See the License for the specific language governing permissions and limitations under the License. ]]-- - module("ffluci.dispatcher", package.seeall) require("ffluci.http") -require("ffluci.template") -require("ffluci.config") require("ffluci.sys") +require("ffluci.fs") --- Sets privilege for given category -function assign_privileges(category) - local cp = ffluci.config.category_privileges - if cp and cp[category] then - local u, g = cp[category]:match("([^:]+):([^:]+)") - ffluci.sys.process.setuser(u) - ffluci.sys.process.setgroup(g) - end -end +-- Local dispatch database +local tree = {nodes={}} +-- Global request object +request = {} --- Builds a URL from a triple of category, module and action -function build_url(category, module, action) - category = category or "public" - module = module or "index" - action = action or "index" - - local pattern = ffluci.http.dispatcher() .. "/%s/%s/%s" - return pattern:format(category, module, action) -end +-- Active dispatched node +dispatched = nil --- Dispatches the "request" -function dispatch(req) - request = req - local m = "ffluci.controller." .. request.category .. "." .. request.module - local stat, module = pcall(require, m) - if not stat then - return error404() - else - module.request = request - module.dispatcher = module.dispatcher or dynamic - setfenv(module.dispatcher, module) - return module.dispatcher(request) - end +-- Builds a URL +function build_url(...) + return ffluci.http.dispatcher() .. "/" .. table.concat(arg, "/") end -- Sends a 404 error code and renders the "error404" template if available @@ -129,6 +48,7 @@ function error404(message) ffluci.http.status(404, "Not Found") message = message or "Not Found" + require("ffluci.template") if not pcall(ffluci.template.render, "error404") then ffluci.http.prepare_content("text/plain") print(message) @@ -140,6 +60,7 @@ end function error500(message) ffluci.http.status(500, "Internal Server Error") + require("ffluci.template") if not pcall(ffluci.template.render, "error500", {message=message}) then ffluci.http.prepare_content("text/plain") print(message) @@ -147,154 +68,124 @@ function error500(message) return false end - -- Dispatches a request depending on the PATH_INFO variable function httpdispatch() local pathinfo = ffluci.http.env.PATH_INFO or "" - local parts = pathinfo:gmatch("/[%w-]+") + local c = tree - local sanitize = function(s, default) - return s and s:sub(2) or default + for s in pathinfo:gmatch("/([%w-]+)") do + table.insert(request, s) end - local cat = sanitize(parts(), "public") - local mod = sanitize(parts(), "index") - local act = sanitize(parts(), "index") - - assign_privileges(cat) - dispatch({category=cat, module=mod, action=act}) -end - - --- Dispatchers -- - - --- The Action Dispatcher searches the module for any function called --- action_"request.action" and calls it -function action(...) - local disp = require("ffluci.dispatcher") - if not disp._action(...) then - disp.error404() - end + dispatch() end --- The CBI dispatcher directly parses and renders the CBI map which is --- placed in ffluci/modles/cbi/"request.module"/"request.action" -function cbi(...) - local disp = require("ffluci.dispatcher") - if not disp._cbi(...) then - disp.error404() +function dispatch() + 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 -end - --- The dynamic dispatcher chains the action, submodule, simpleview and CBI dispatcher --- in this particular order. It is the default dispatcher. -function dynamic(...) - local disp = require("ffluci.dispatcher") - if not disp._action(...) - and not disp._submodule(...) - and not disp._simpleview(...) - and not disp._cbi(...) then - disp.error404() + + + if track.i18n then + require("ffluci.i18n").loadc(track.i18n) end -end - --- The Simple View Dispatcher directly renders the template --- which is placed in ffluci/views/"request.module"/"request.action" -function simpleview(...) - local disp = require("ffluci.dispatcher") - if not disp._simpleview(...) then - disp.error404() + + if track.setuser then + ffluci.sys.process.setuser(track.setuser) end -end - - --- The submodule dispatcher tries to load a submodule of the controller --- and calls its "action"-function -function submodule(...) - local disp = require("ffluci.dispatcher") - if not disp._submodule(...) then - disp.error404() + + if track.setgroup then + ffluci.sys.process.setgroup(track.setgroup) end -end - - --- Internal Dispatcher Functions -- - -function _action(request) - local action = getfenv(2)["action_" .. request.action:gsub("-", "_")] - local i18n = require("ffluci.i18n") - if action then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - action() - return true + + if c and type(c.target) == "function" then + dispatched = c + + stat, err = pcall(c.target) + if not stat then + error500(err) + end else - return false + error404() end end -function _cbi(request) - local disp = require("ffluci.dispatcher") - local tmpl = require("ffluci.template") - local cbi = require("ffluci.cbi") - local i18n = require("ffluci.i18n") - - local path = request.category.."_"..request.module.."/"..request.action +-- Calls the index function of all available controllers +function createindex() + local root = ffluci.sys.libpath() .. "/controller/" + local suff = ".lua" + for i,c in ipairs(ffluci.fs.glob(root .. "*/*" .. suff)) do + c = "ffluci.controller." .. c:sub(#root+1, #c-#suff):gsub("/", ".", 1) + stat, mod = pcall(require, c) - local stat, map = pcall(cbi.load, path) - if stat and map then - local stat, err = pcall(map.parse, map) - if not stat then - disp.error500(err) - return true + if stat and mod and type(mod.index) == "function" then + ffluci.util.updfenv(mod.index, ffluci.dispatcher) + pcall(mod.index) end - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - tmpl.render("cbi/header") - map:render() - tmpl.render("cbi/footer") - return true - elseif not stat then - disp.error500(map) - return true - else - return false end end -function _simpleview(request) - local i18n = require("ffluci.i18n") - local tmpl = require("ffluci.template") +-- Fetch a dispatching node +function node(...) + local c = tree - local path = request.category.."_"..request.module.."/"..request.action + for k,v in ipairs(arg) do + if not c.nodes[v] then + c.nodes[v] = {nodes={}} + end + + c = c.nodes[v] + end - local stat, t = pcall(tmpl.Template, path) - if stat then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - t:render() - return true - else - return false + return c +end + +-- Subdispatchers -- +function alias(...) + local req = arg + return function() + request = req + dispatch() end end +function template(name) + require("ffluci.template") + return function() ffluci.template.render(name) end +end -function _submodule(request) - local i18n = require("ffluci.i18n") - local m = "ffluci.controller." .. request.category .. "." .. - request.module .. "." .. request.action - local stat, module = pcall(require, m) +function cbi(model) + require("ffluci.cbi") + require("ffluci.template") - if stat and module.action then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - return pcall(module.action) + return function() + local stat, res = pcall(ffluci.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 + + ffluci.template.render("cbi/header") + res:render() + ffluci.template.render("cbi/footer") end - - return false end
\ No newline at end of file diff --git a/core/src/i18n/default.en b/core/src/i18n/default.en index 726095ae7..d76b770e9 100644 --- a/core/src/i18n/default.en +++ b/core/src/i18n/default.en @@ -10,13 +10,5 @@ apply = "Apply" changes = "Changes" revert = "Revert" -index = "Overview" -system = "System" -services = "Services" -network = "Network" -wifi = "Wifi" -status = "Status" -statistic = "Statistic" - config = "Configuration" path = "Path"
\ No newline at end of file diff --git a/core/src/init.lua b/core/src/init.lua index 8d4cea2e9..f54854e4e 100644 --- a/core/src/init.lua +++ b/core/src/init.lua @@ -25,5 +25,5 @@ limitations under the License. ]]-- module("ffluci", package.seeall) -__version__ = "0.4" +__version__ = "0.5" __appname__ = "FFLuCI" diff --git a/core/src/menu.lua b/core/src/menu.lua index d6f65116e..9328e332c 100644 --- a/core/src/menu.lua +++ b/core/src/menu.lua @@ -28,6 +28,7 @@ module("ffluci.menu", package.seeall) require("ffluci.fs") require("ffluci.util") require("ffluci.sys") +require("ffluci.dispatcher") -- Default modelpath modelpattern = ffluci.sys.libpath() .. "/model/menu/*.lua" @@ -39,98 +40,7 @@ scope = { isfile = ffluci.fs.isfile } --- Local menu database -local menu = nil - --- The current pointer -local menuc = {} - --- Adds a menu category to the current menu and selects it -function add(cat, controller, title, order) - order = order or 100 - if not menu[cat] then - menu[cat] = {} - end - - local entry = {} - entry[".descr"] = title - entry[".order"] = order - entry[".contr"] = controller - - menuc = entry - - local i = 0 - for k,v in ipairs(menu[cat]) do - if v[".order"] > entry[".order"] then - break - end - i = k - end - table.insert(menu[cat], i+1, entry) - - return true -end - --- Adds an action to the current menu -function act(action, title) - table.insert(menuc, {action = action, descr = title}) - return true -end - --- Selects a menu category -function sel(cat, controller) - if not menu[cat] then - return nil - end - menuc = menu[cat] - - local stat = nil - for k,v in ipairs(menuc) do - if v[".contr"] == controller then - menuc = v - stat = true - end - end - - return stat -end - - --- Collect all menu information provided in the model dir -function collect() - local generators = {} - - local m = ffluci.fs.glob(modelpattern) or {} - for k, menu in pairs(m) do - local f = loadfile(menu) - if f then - table.insert(generators, f) - end - end - - return generators -end - --- Parse the collected information -function parse(generators) - menu = {} - for i, f in pairs(generators) do - local env = ffluci.util.clone(scope) - - env.add = add - env.sel = sel - env.act = act - - setfenv(f, env) - f() - end - return menu -end - -- Returns the menu information function get() - if not menu then - menu = parse(collect()) - end return menu end
\ No newline at end of file diff --git a/core/src/template.lua b/core/src/template.lua index 6f4e4adb0..a56b49d83 100644 --- a/core/src/template.lua +++ b/core/src/template.lua @@ -55,7 +55,7 @@ viewns = { controller = ffluci.http.dispatcher(), uploadctrl = ffluci.http.dispatcher_upload(), media = ffluci.config.main.mediaurlbase, - images = ffluci.config.main.imagebase, + resource = ffluci.config.main.resourcebase, write = io.write, include = function(name) Template(name):render(getfenv(2)) end, } diff --git a/core/src/view/footer.htm b/core/src/view/footer.htm index 67856771b..f324408a1 100644 --- a/core/src/view/footer.htm +++ b/core/src/view/footer.htm @@ -2,6 +2,6 @@ <div class="clear"></div> </div></div> -<div class="separator magenta bold"><a href="http://luci.freifunk-halle.net">FFLuCI 0.3 - Freifunk Lua Configuration Interface</a></div> +<div class="separator magenta bold"><a href="http://luci.freifunk-halle.net"><%=require("ffluci").__appname__ .. " " .. ffluci.__version__%> - Freifunk Lua Configuration Interface</a></div> </body> </html>
\ No newline at end of file diff --git a/core/src/view/header.htm b/core/src/view/header.htm index 9bb8b8be9..99b43805d 100644 --- a/core/src/view/header.htm +++ b/core/src/view/header.htm @@ -1,21 +1,33 @@ <% require("ffluci.sys") local load1, load5, load15 = ffluci.sys.loadavg() -local req = require("ffluci.dispatcher").request -local menu = require("ffluci.menu").get()[req.category] -menu = menu or {} + +local request = require("ffluci.dispatcher").request +local category = request[1] +local tree = ffluci.dispatcher.node() +local cattree = category and ffluci.dispatcher.node(category) +local node = ffluci.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("ffluci.i18n").loadc("default") + require("ffluci.http").prepare_content("text/html") %><?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link rel="stylesheet" type="text/css" href="<%=media%>/cascade.css" /> - <link rel="stylesheet" type="text/css" href="<%=media%>/css/<%=req.category%>_<%=req.module%>.css" /> - <link rel="stylesheet" type="text/css" href="<%=media%>/css/<%=req.category%>_<%=req.module%>_<%=req.action%>.css" /> + <% if node and node.css then %><link rel="stylesheet" type="text/css" href="<%=resource%>/<%=node.css%>" /><% end %> <meta http-equiv="content-type" content="text/xhtml+xml; charset=utf-8" /> <meta http-equiv="content-script-type" content="text/javascript" /> - <title>FFLuCI</title> + <title>FFLuCI - Freifunk Lua Configuration Interface</title> </head> <body> <div id="header"> @@ -27,37 +39,82 @@ require("ffluci.http").prepare_content("text/html") <%:hostname Hostname%>: <%=ffluci.sys.hostname()%> </div> <div> - <span class="headertitle">Freifunk Kamikaze</span><br /> - <span class="whitetext bold"><%:batmanedition Fledermausedition%></span> + <span class="headertitle"><%~luci.main.title%></span><br /> + <span class="whitetext bold"><%~luci.main.subtitle%></span> </div> </div> <div class="separator yellow bold"> -<%:path Pfad%>: <a href="<%=controller .. "/" .. req.category%>"><%=translate(req.category, req.category)%></a> -» <a href="<%=controller .. "/" .. req.category .. "/" .. req.module %>"><%=translate(req.module, req.module)%></a> -» <a href="<%=controller .. "/" .. req.category .. "/" .. req.module .. "/" .. req.action %>"><%=translate(req.action, req.action)%></a> +<%: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 + %><a href="<%=url%>"><%=c.title or v%></a> <% if k ~= #request then %>» <% end + end +end +%> </div> <div id="columns"><div id="columnswrapper"> <div class="sidebar left"> - <% for k,v in pairs(menu) do %> - <div<% if v[".contr"] == req.module then %> class="yellowtext"<% end %>><a href="<%=controller.."/"..req.category.."/"..v[".contr"]%>"><%=translate(v[".contr"], v[".descr"])%></a><% - if v[".contr"] == req.module then %> - <ul><% for key,val in ipairs(v) do %> - <li<% if val.action == req.action then %> class="yellowtext"<% end %>><a href="<%=controller.."/"..req.category.."/"..req.module.."/"..val.action%>"><%=translate(val.action, val.descr)%></a></li> - <% end %></ul> - <% end %></div> +<% +local function submenu(prefix, node) + if not node._menu_selected or not node.nodes then + return false + end + local index = {} + for k, n in pairs(node.nodes) do + table.insert(index, {name=k, order=n.order or 100}) + end + + table.sort(index, function(a, b) return a.order < b.order end) +%> + <ul> + <% for j, v in pairs(index) do + local nnode = node.nodes[v.name]%> + <li> + <span<% if nnode._menu_selected then %> class="yellowtext"<%end%>><a href="<%=controller .. prefix .. v.name%>"><%=nnode.title%></a></span> + <% submenu(prefix .. v.name .. "/", nnode) %> + </li> <% end %> + </ul> +<% +end + +if cattree and cattree.nodes then + local index = {} + for k, node in pairs(cattree.nodes) do + table.insert(index, {name=k, order=node.order or 100}) + end + + table.sort(index, function(a, b) return a.order < b.order end) + + for i, k in ipairs(index) do + node = cattree.nodes[k.name] + if node.title then %> + <div<% if node._menu_selected then %> class="yellowtext"<%end%>><a href="<%=controller%>/<%=category%>/<%=k.name%>"><%=node.title%></a> + <%submenu("/" .. category .. "/" .. k.name .. "/", node)%> + </div> +<% end + end +end +%> </div> <div class="sidebar right"> <div><%:webif Weboberfläche%> - <ul> - <li<% if "public" == req.category then %> class="yellowtext"<% end %>><a href="<%=controller%>/public"><%:public Ă–ffentlich%></a></li> - <li<% if "admin" == req.category then %> class="yellowtext"<% end %>><a href="<%=controller%>/admin"><%:admin Verwaltung%></a></li> + <ul><% + for k,node in pairs(tree.nodes) do + if node.title then %> + <li<% if request[1] == k then %> class="yellowtext"<%end%>><a href="<%=controller%>/<%=k%>"><%=node.title%></a></li> +<% end + end%> </ul> </div> <% - if "admin" == req.category then + if "admin" == request[1] then require("ffluci.model.uci") local ucic = ffluci.model.uci.changes() if ucic then |