2 FFLuCI - Configuration Bind Interface
5 Offers an interface for binding confiugration values to certain
6 data types. Supports value and range validation and basic dependencies.
12 Copyright 2008 Steven Barth <steven@midlink.org>
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at
18 http://www.apache.org/licenses/LICENSE-2.0
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
27 module("ffluci.cbi", package.seeall)
29 require("ffluci.template")
30 require("ffluci.util")
31 require("ffluci.http")
32 require("ffluci.model.uci")
34 local Template = ffluci.template.Template
35 local class = ffluci.util.class
36 local instanceof = ffluci.util.instanceof
41 require("ffluci.i18n")
43 local cbidir = ffluci.fs.dirname(ffluci.util.__file__()) .. "model/cbi/"
44 local func = loadfile(cbidir..cbimap..".lua")
47 error("Unable to load CBI map: " .. cbimap)
51 ffluci.util.resfenv(func)
52 ffluci.util.updfenv(func, ffluci.cbi)
53 ffluci.util.extfenv(func, "translate", ffluci.i18n.translate)
57 if not instanceof(map, Map) then
58 error("CBI map returns no valid map object!")
65 -- Node pseudo abstract class
68 function Node.__init__(self, title, description)
70 self.title = title or ""
71 self.description = description or ""
72 self.template = "cbi/node"
75 function Node.append(self, obj)
76 table.insert(self.children, obj)
79 function Node.parse(self)
80 for k, child in ipairs(self.children) do
85 function Node.render(self)
86 ffluci.template.render(self.template, {self=self})
89 function Node.render_children(self)
90 for k, node in ipairs(self.children) do
97 Map - A map describing a configuration file
101 function Map.__init__(self, config, ...)
102 Node.__init__(self, ...)
104 self.template = "cbi/map"
107 function Map.parse(self)
108 self.ucidata = ffluci.model.uci.show(self.config)
109 if not self.ucidata then
110 error("Unable to read UCI data: " .. self.config)
112 self.ucidata = self.ucidata[self.config]
117 function Map.render(self)
118 self.ucidata = ffluci.model.uci.show(self.config)
119 if not self.ucidata then
120 error("Unable to read UCI data: " .. self.config)
122 self.ucidata = self.ucidata[self.config]
127 function Map.section(self, class, ...)
128 if instanceof(class, AbstractSection) then
129 local obj = class(...)
131 obj.config = self.config
135 error("class must be a descendent of AbstractSection")
142 AbstractSection = class(Node)
144 function AbstractSection.__init__(self, sectiontype, ...)
145 Node.__init__(self, ...)
146 self.sectiontype = sectiontype
149 function AbstractSection.option(self, class, ...)
150 if instanceof(class, AbstractValue) then
151 local obj = class(...)
153 obj.config = self.config
157 error("class must be a descendent of AbstractValue")
164 NamedSection - A fixed configuration section defined by its name
166 NamedSection = class(AbstractSection)
168 function NamedSection.__init__(self, section, ...)
169 AbstractSection.__init__(self, ...)
170 self.template = "cbi/nsection"
172 self.section = section
175 function NamedSection.option(self, ...)
176 local obj = AbstractSection.option(self, ...)
177 obj.section = self.section
183 TypedSection - A (set of) configuration section(s) defined by the type
184 addremove: Defines whether the user can add/remove sections of this type
185 anonymous: Allow creating anonymous sections
186 valid: a table with valid names or a function returning nil if invalid
188 TypedSection = class(AbstractSection)
190 function TypedSection.__init__(self, ...)
191 AbstractSection.__init__(self, ...)
192 self.template = "cbi/tsection"
194 self.addremove = true
195 self.anonymous = false
199 function TypedSection.parse(self)
200 for k, v in pairs(self:ucisections()) do
201 for i, node in ipairs(self.children) do
208 function TypedSection.render_children(self, section)
209 for k, node in ipairs(self.children) do
210 node.section = section
215 function TypedSection.ucisections(self)
217 for k, v in pairs(self.map.ucidata) do
218 if v[".type"] == self.sectiontype then
227 AbstractValue - An abstract Value Type
228 null: Value can be empty
229 valid: A function returning the value if it is valid otherwise nil
230 depends: A table of option => value pairs of which one must be true
231 default: The default value
233 AbstractValue = class(Node)
235 function AbstractValue.__init__(self, option, ...)
236 Node.__init__(self, ...)
244 function AbstractValue.formvalue(self)
245 local key = "cbid."..self.map.config.."."..self.section.."."..self.option
246 return ffluci.http.formvalue(key)
249 function AbstractValue.parse(self)
250 local fvalue = self:validate(self:formvalue())
251 if fvalue and not (fvalue == self:ucivalue()) then
256 function AbstractValue.ucivalue(self)
257 return self.map.ucidata[self.section][self.option]
260 function AbstractValue.validate(self, value)
261 return ffluci.util.validate(value, nil, nil, self.valid)
264 function AbstractValue.write(self, value)
265 return ffluci.model.uci.set(self.config, self.section, self.option, value)
270 Value - A one-line value
271 maxlength: The maximum length
272 isnumber: The value must be a valid (floating point) number
273 isinteger: The value must be a valid integer
275 Value = class(AbstractValue)
277 function Value.__init__(self, ...)
278 AbstractValue.__init__(self, ...)
279 self.template = "cbi/value"
282 self.isnumber = false
283 self.isinteger = false
288 ListValue - A one-line value predefined in a list
290 ListValue = class(AbstractValue)
292 function ListValue.__init__(self, ...)
293 AbstractValue.__init__(self, ...)
294 self.template = "cbi/lvalue"
299 function ListValue.addValue(self, key, val)