* ffluci.template: fixed a bug where different template scopes were not separated correctly
* Added ffluci.dispatcher.cbi and ffluci.dispatcher.dynamic
]]--
module("ffluci.cbi", package.seeall)
+
require("ffluci.template")
require("ffluci.util")
require("ffluci.http")
require("ffluci.model.uci")
-local Template = ffluci.template.Template
-local class = ffluci.util.class
+
+local Template = ffluci.template.Template
+local class = ffluci.util.class
local instanceof = ffluci.util.instanceof
+function load(cbimap)
+ require("ffluci.fs")
+ require("ffluci.i18n")
+
+ local cbidir = ffluci.fs.dirname(ffluci.util.__file__()) .. "model/cbi/"
+ local func = loadfile(cbidir..cbimap..".lua")
+
+ if not func then
+ error("Unable to load CBI map: " .. cbimap)
+ return nil
+ end
+
+ ffluci.util.resfenv(func)
+ ffluci.util.updfenv(func, ffluci.cbi)
+ ffluci.util.extfenv(func, "translate", ffluci.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
- self.description = description
+ self.title = title or ""
+ self.description = description or ""
self.template = "cbi/node"
end
end
function Node.render(self)
- ffluci.template.render(self.template)
+ ffluci.template.render(self.template, {self=self})
end
end
function Map.read(self)
- self.ucidata = self.ucidata or ffluci.model.uci.show(self.config)
+ self.ucidata = self.ucidata or ffluci.model.uci.show(self.config)[self.config]
return self.ucidata
end
end
function AbstractValue.ucivalue(self)
- return self.map.read()[self.section][self.option]
+ return self.map:read()[self.section][self.option]
end
function AbstractValue.validate(self, value)
-module(..., package.seeall)
-dispatcher = require("ffluci.dispatcher").simpleview
\ No newline at end of file
+module(..., package.seeall)
\ No newline at end of file
return error404()
else
module.request = request
+ module.dispatcher = module.dispatcher or dynamic
setfenv(module.dispatcher, module)
return module.dispatcher(request)
end
dispatch({category=cat, module=mod, action=act})
end
--- The Simple View Dispatcher directly renders the template
--- which is placed in ffluci/views/"request.module"/"request.action"
-function simpleview(request)
+
+-- Dispatchers --
+
+
+-- The Action Dispatcher searches the module for any function called
+-- action_"request.action" and calls it
+function action(request)
local i18n = require("ffluci.i18n")
- local tmpl = require("ffluci.template")
local disp = require("ffluci.dispatcher")
i18n.loadc(request.module)
- if not pcall(tmpl.render, request.module .. "/" .. request.action) then
+ local action = getfenv()["action_" .. request.action:gsub("-", "_")]
+ if action then
+ action()
+ else
disp.error404()
end
end
--- The Action Dispatcher searches the module for any function called
--- action_"request.action" and calls it
-function action(request)
+-- The CBI dispatcher directly parses and renders the CBI map which is
+-- placed in ffluci/modles/cbi/"request.module"/"request.action"
+function cbi(request)
local i18n = require("ffluci.i18n")
local disp = require("ffluci.dispatcher")
+ local tmpl = require("ffluci.template")
+ local cbi = require("ffluci.cbi")
i18n.loadc(request.module)
+
+ stat, map = pcall(cbi.load, request.module.."/"..request.action)
+ if stat then
+ tmpl.render("header")
+ map:render()
+ tmpl.render("footer")
+ else
+ disp.error404()
+ end
+end
+
+-- The dynamic dispatchers combines the action, simpleview and cbi dispatchers
+-- in one dispatcher. It tries to lookup the request in this order.
+function dynamic(request)
+ local i18n = require("ffluci.i18n")
+ local disp = require("ffluci.dispatcher")
+ local tmpl = require("ffluci.template")
+ local cbi = require("ffluci.cbi")
+
+ i18n.loadc(request.module)
+
local action = getfenv()["action_" .. request.action:gsub("-", "_")]
if action then
action()
- else
+ return
+ end
+
+ if pcall(tmpl.render, request.module .. "/" .. request.action) then
+ return
+ end
+
+ stat, map = pcall(cbi.load, request.module.."/"..request.action)
+ if stat then
+ tmpl.render("header")
+ map:render()
+ tmpl.render("footer")
+ return
+ end
+
+ disp.error404()
+end
+
+-- The Simple View Dispatcher directly renders the template
+-- which is placed in ffluci/views/"request.module"/"request.action"
+function simpleview(request)
+ local i18n = require("ffluci.i18n")
+ local tmpl = require("ffluci.template")
+ local disp = require("ffluci.dispatcher")
+
+ i18n.loadc(request.module)
+ if not pcall(tmpl.render, request.module .. "/" .. request.action) then
disp.error404()
end
end
\ No newline at end of file
end
-- Oldstyle render shortcut
-function render(name, ...)
+function render(name, scope, ...)
+ scope = scope or getfenv(2)
local s, t = pcall(Template, name)
if not s then
error("Unable to load template: " .. name)
else
- t:render(...)
+ t:render(scope, ...)
end
end
local oldfenv = getfenv(self.template)
-- Put our predefined objects in the scope of the template
+ ffluci.util.resfenv(self.template)
ffluci.util.updfenv(self.template, scope)
ffluci.util.updfenv(self.template, self.viewns)
function extfenv(f, key, obj)
local scope = getfenv(f)
scope[key] = obj
- setfenv(f, scope)
end
end
+-- Resets the scope of f doing a shallow copy of its scope into a new table
+function resfenv(f)
+ local scope = getfenv(f)
+ setfenv(f, {})
+ updfenv(f, scope)
+end
+
+
-- Updates the scope of f with "extscope"
function updfenv(f, extscope)
local scope = getfenv(f)
for k, v in pairs(extscope) do
scope[k] = v
end
- setfenv(f, scope)
end
-<div class="cbi-lvalue">
-<div class="cbi-lvalue-title"><%=self.title%></div>
-<div class="cbi-lvalue-field">
-<select name="<%=self.config.."."..self.section.."."..self.option%>">
-<%for k, v in self.list do%>
-<option value="<%=k%>"><%=v%></option>
+ <div class="cbi-lvalue">
+ <div class="cbi-lvalue-title"><%=self.title%></div>
+ <div class="cbi-lvalue-field">
+ <select name="cbid.<%=self.config.."."..self.section.."."..self.option%>">
+<%for k, v in pairs(self.list) do%>
+ <option value="<%=k%>"><%=v%></option>
<% end %>
-</select>
-</div>
-<div class="cbi-value-description"><%=self.description%></div>
-</div>
\ No newline at end of file
+ </select>
+ </div>
+ <div class="cbi-value-description"><%=self.description%></div>
+ </div>
\ No newline at end of file
-<div class="cbi-map" id="cbi-<%=self.config%>">
-<form method="post" action="<%=os.getenv("REQUEST_URI")%>">
-<h1><%=self.title%></h1>
-<div class="cbi-map-descr"><%=self.description%></div>
+ <div class="cbi-map" id="cbi-<%=self.config%>">
+ <form method="post" action="<%=os.getenv("REQUEST_URI")%>">
+ <h1><%=self.title%></h1>
+ <div class="cbi-map-descr"><%=self.description%></div>
+ <br />
<% for k, node in ipairs(self.children) do node:render() end %>
-</form>
-</div>
+ <br />
+ <input type="submit" /> <input type="reset" />
+ </form>
+ </div>
-<div class="cbi-nsection" id="cbi-<%=self.config%>-<%=self.sectiontype%>-<%=self.section%>">
-<h2><%=self.title%></h2>
-<div class="cbi-nsection-descr"><%=self.description%></div>
+ <div class="cbi-nsection" id="cbi-<%=self.config%>-<%=self.section%>">
+ <h2><%=self.title%></h2>
+ <div class="cbi-nsection-descr"><%=self.description%></div>
+ <div class="cbi-nsection-options">
<% for k, node in ipairs(self.children) do node:render() end %>
-</div>
+ </div>
+ </div>
local allsections = self.map:read()
local sections = {}
for k, v in pairs(allsections) do
- if v[".type"] == sectiontype then
+ if v[".type"] == self.sectiontype then
sections[k] = v
end
end
%>
-<div class="cbi-tsection" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
-<h2><%=self.title%></h2>
-<div class="cbi-tsection-descr"><%=self.description%></div>
+ <div class="cbi-tsection" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
+ <h2><%=self.title%></h2>
+ <div class="cbi-tsection-descr"><%=self.description%></div>
<% for k, v in pairs(sections) do %>
-<div class="cbi-tsection-node" id="cbi-<%=self.config%>-<%=k%>">
-<% for k, node in ipairs(self.children) do
+ <div class="cbi-tsection-node" id="cbi-<%=self.config%>-<%=k%>">
+<% for i, node in ipairs(self.children) do
node.section = k
- node:render(k)
+ node:render()
end %>
-</div>
+ </div>
<% end %>
-</div>
+ </div>
-<div class="cbi-value">
-<div class="cbi-value-title"><%=self.title%></div>
-<div class="cbi-value-field">
-<input type="text" name="<%=self.config.."."..self.section.."."..self.option%>" value="<%=self:ucivalue()%>" />
-</div>
-<div class="cbi-value-description"><%=self.description%></div>
-</div>
\ No newline at end of file
+ <div class="cbi-value">
+ <div class="cbi-value-title"><%=self.title%></div>
+ <div class="cbi-value-field">
+ <input type="text" name="cbid.<%=self.config.."."..self.section.."."..self.option%>" value="<%=(self:ucivalue() or "")%>" />
+ </div>
+ <div class="cbi-value-description"><%=self.description%></div>
+ </div>
\ No newline at end of file