libs/cbi: Major Improvements

Added initial support for non-UCI-based forms (luci.cbi.SimpleForm)
	Minor API improvements
	Now correctly tagging "empty mandatory field" errors
This commit is contained in:
Steven Barth 2008-08-09 14:14:04 +00:00
parent b71825db91
commit bdb4bbde13
8 changed files with 169 additions and 7 deletions

View file

@ -1,6 +1,7 @@
cbi_add = "Add entry" cbi_add = "Add entry"
cbi_del = "Remove entry" cbi_del = "Remove entry"
cbi_invalid = "Error: Invalid input value" cbi_invalid = "Error: Invalid input value"
cbi_missing = "Error: This field is mandatory"
cbi_addopt = "-- Additional Field --" cbi_addopt = "-- Additional Field --"
cbi_optional = " (optional)" cbi_optional = " (optional)"
cbi_sectempty = "This section contains no values yet" cbi_sectempty = "This section contains no values yet"

View file

@ -89,6 +89,7 @@ settings = "Settings"
start = "Start" start = "Start"
static = "static" static = "static"
statistics = "Statistics" statistics = "Statistics"
submit = "Submit"
syslog = "System Log" syslog = "System Log"
system = "System" system = "System"

View file

@ -1,6 +1,7 @@
cbi_add = "Eintrag hinzufügen" cbi_add = "Eintrag hinzufügen"
cbi_del = "Eintrag entfernen" cbi_del = "Eintrag entfernen"
cbi_invalid = "Error: Ungültige Eingabe" cbi_invalid = "Fehler: Ungültige Eingabe"
cbi_missing = "Fehler: Dieses Feld muss ausgefüllt werden"
cbi_addopt = "-- Zusätzliches Feld --" cbi_addopt = "-- Zusätzliches Feld --"
cbi_sectempty = "Diese Sektion enthält noch keine Einträge" cbi_sectempty = "Diese Sektion enthält noch keine Einträge"
cbi_manual = "-- benutzerdefiniert --" cbi_manual = "-- benutzerdefiniert --"

View file

@ -90,6 +90,7 @@ settings = "Einstellungen"
start = "Start" start = "Start"
static = "statisch" static = "statisch"
statistics = "Statistiken" statistics = "Statistiken"
submit = "Absenden"
syslog = "Systemprotokoll" syslog = "Systemprotokoll"
system = "System" system = "System"

View file

@ -35,6 +35,9 @@ local uci = luci.model.uci
local class = luci.util.class local class = luci.util.class
local instanceof = luci.util.instanceof local instanceof = luci.util.instanceof
FORM_NODATA = 0
FORM_VALID = 1
FORM_INVALID = -1
-- Loads a CBI map from given file, creating an environment and returns it -- Loads a CBI map from given file, creating an environment and returns it
function load(cbimap, ...) function load(cbimap, ...)
@ -61,7 +64,7 @@ function load(cbimap, ...)
local maps = {func()} local maps = {func()}
for i, map in ipairs(maps) do for i, map in ipairs(maps) do
if not instanceof(map, Map) then if not instanceof(map, Node) then
error("CBI map returns no valid map object!") error("CBI map returns no valid map object!")
return nil return nil
end end
@ -231,6 +234,68 @@ function Map.get(self, section, option)
end end
--[[
SimpleForm - A Simple non-UCI form
]]--
SimpleForm = class(Node)
function SimpleForm.__init__(self, config, title, description, data)
Node.__init__(self, title, description)
self.config = config
self.data = data or {}
self.template = "cbi/simpleform"
self.dorender = true
end
function SimpleForm.parse(self, ...)
Node.parse(self, 1, ...)
local valid = true
for i, v in ipairs(self.children) do
valid = valid and not v.tag_missing[1] and not v.tag_invalid[1]
end
local state =
not luci.http.formvalue("cbi.submit") and 0
or valid and 1
or -1
self.dorender = self:handle(state)
end
function SimpleForm.render(self, ...)
if self.dorender then
Node.render(self, ...)
end
end
-- Creates a child section
function SimpleForm.field(self, class, ...)
if instanceof(class, AbstractValue) then
local obj = class(self, ...)
self:append(obj)
return obj
else
error("class must be a descendent of AbstractValue")
end
end
function SimpleForm.set(self, section, option, value)
self.data[option] = value
end
function SimpleForm.del(self, section, option)
self.data[option] = nil
end
function SimpleForm.get(self, section, option)
return self.data[option]
end
--[[ --[[
AbstractSection AbstractSection
]]-- ]]--
@ -534,12 +599,13 @@ function AbstractValue.__init__(self, map, option, ...)
self.map = map self.map = map
self.config = map.config self.config = map.config
self.tag_invalid = {} self.tag_invalid = {}
self.tag_missing = {}
self.deps = {} self.deps = {}
self.rmempty = false self.rmempty = false
self.default = nil self.default = nil
self.size = nil self.size = nil
self.optional = false self.optional = false
end end
-- Add a dependencie to another section field -- Add a dependencie to another section field
@ -559,11 +625,20 @@ function AbstractValue.formvalue(self, section)
return luci.http.formvalue(key) return luci.http.formvalue(key)
end end
function AbstractValue.additional(self, value)
self.optional = value
end
function AbstractValue.mandatory(self, value)
self.rmempty = not value
end
function AbstractValue.parse(self, section) function AbstractValue.parse(self, section)
local fvalue = self:formvalue(section) local fvalue = self:formvalue(section)
local cvalue = self:cfgvalue(section)
if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
fvalue = self:validate(fvalue) fvalue = self:transform(self:validate(fvalue))
if not fvalue then if not fvalue then
self.tag_invalid[section] = true self.tag_invalid[section] = true
end end
@ -573,6 +648,8 @@ function AbstractValue.parse(self, section)
else -- Unset the UCI or error else -- Unset the UCI or error
if self.rmempty or self.optional then if self.rmempty or self.optional then
self:remove(section) self:remove(section)
elseif not fvalue or fvalue ~= cvalue then
self.tag_missing[section] = true
end end
end end
end end
@ -618,6 +695,9 @@ function AbstractValue.validate(self, value)
return value return value
end end
AbstractValue.transform = AbstractValue.validate
-- Write to UCI -- Write to UCI
function AbstractValue.write(self, section, value) function AbstractValue.write(self, section, value)
return self.map:set(section, self.option, value) return self.map:set(section, self.option, value)

View file

@ -21,6 +21,9 @@ $Id$
<% if self.tag_invalid[section] then -%> <% if self.tag_invalid[section] then -%>
<div class="cbi-error"><%:cbi_invalid%></div> <div class="cbi-error"><%:cbi_invalid%></div>
<%- end %> <%- end %>
<% if self.tag_missing[section] then -%>
<div class="cbi-error"><%:cbi_missing%></div>
<%- end %>
</div> </div>
<% if #self.deps > 0 then -%> <% if #self.deps > 0 then -%>

View file

@ -0,0 +1,46 @@
<%#
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
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
$Id$
-%>
<form method="post" action="<%=luci.http.getenv("REQUEST_URI")%>">
<div>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<input type="hidden" name="cbi.submit" value="1" />
<input type="submit" value="<%:save%>" class="hidden" />
</div>
<div class="cbi-map" id="cbi-<%=self.config%>">
<h1><%=self.title%></h1>
<div class="cbi-map-descr"><%=self.description%></div>
<fieldset class="cbi-section">
<div class="cbi-section-node">
<% self:render_children(1, scope or {}) %>
</div>
<br />
</fieldset>
<br />
</div>
<div>
<%- if self.submit ~= false then %>
<input type="submit" value="
<%- if not self.submit then -%><%-:submit-%><%-else-%><%=self.submit%><%end%>
" />
<% end %>
<%- if self.reset ~= false then %>
<input type="reset" value="
<%- if not self.reset then -%><%-:reset-%><%-else-%><%=self.reset%><%end%>
" />
<% end %>
<script type="text/javascript">cbi_d_init();</script>
</div>
</form>

View file

@ -461,3 +461,32 @@ function cbi(model)
luci.template.render("cbi/footer") luci.template.render("cbi/footer")
end end
end end
--- Create a CBI form model dispatching target.
-- @param model CBI form model tpo be rendered
function form(model)
require("luci.cbi")
require("luci.template")
return function(...)
local stat, maps = luci.util.copcall(luci.cbi.load, model, ...)
if not stat then
error500(maps)
return true
end
for i, res in ipairs(maps) do
local stat, err = luci.util.copcall(res.parse, res)
if not stat then
error500(err)
return true
end
end
luci.template.render("header")
for i, res in ipairs(maps) do
res:render()
end
luci.template.render("footer")
end
end