5a20aadc53765dd01a9b5240d50e6802ac2c25ed
[project/luci.git] / src / ffluci / cbi.lua
1 --[[
2 FFLuCI - Configuration Bind Interface
3
4 Description:
5 Offers an interface for binding confiugration values to certain
6 data types. Supports value and range validation and basic dependencies.
7
8 FileId:
9 $Id$
10
11 License:
12 Copyright 2008 Steven Barth <steven@midlink.org>
13
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
17
18 http://www.apache.org/licenses/LICENSE-2.0
19
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.
25
26 ]]--
27 module("ffluci.cbi", package.seeall)
28
29 require("ffluci.template")
30 require("ffluci.util")
31 require("ffluci.http")
32 require("ffluci.model.uci")
33
34 local Template = ffluci.template.Template
35 local class = ffluci.util.class
36 local instanceof = ffluci.util.instanceof
37
38
39 function load(cbimap)
40 require("ffluci.fs")
41 require("ffluci.i18n")
42
43 local cbidir = ffluci.fs.dirname(ffluci.util.__file__()) .. "model/cbi/"
44 local func = loadfile(cbidir..cbimap..".lua")
45
46 if not func then
47 error("Unable to load CBI map: " .. cbimap)
48 return nil
49 end
50
51 ffluci.util.resfenv(func)
52 ffluci.util.updfenv(func, ffluci.cbi)
53 ffluci.util.extfenv(func, "translate", ffluci.i18n.translate)
54
55 local map = func()
56
57 if not instanceof(map, Map) then
58 error("CBI map returns no valid map object!")
59 return nil
60 end
61
62 return map
63 end
64
65 -- Node pseudo abstract class
66 Node = class()
67
68 function Node.__init__(self, title, description)
69 self.children = {}
70 self.title = title or ""
71 self.description = description or ""
72 self.template = "cbi/node"
73 end
74
75 function Node.append(self, obj)
76 table.insert(self.children, obj)
77 end
78
79 function Node.parse(self)
80 for k, child in ipairs(self.children) do
81 child:parse()
82 end
83 end
84
85 function Node.render(self)
86 ffluci.template.render(self.template, {self=self})
87 end
88
89
90 --[[
91 Map - A map describing a configuration file
92 ]]--
93 Map = class(Node)
94
95 function Map.__init__(self, config, ...)
96 Node.__init__(self, ...)
97 self.config = config
98 self.template = "cbi/map"
99 end
100
101 function Map.section(self, class, ...)
102 if instanceof(class, AbstractSection) then
103 local obj = class(...)
104 obj.map = self
105 obj.config = self.config
106 self:append(obj)
107 return obj
108 else
109 error("class must be a descendent of AbstractSection")
110 end
111 end
112
113 function Map.read(self)
114 self.ucidata = self.ucidata or ffluci.model.uci.show(self.config)[self.config]
115 return self.ucidata
116 end
117
118 --[[
119 AbstractSection
120 ]]--
121 AbstractSection = class(Node)
122
123 function AbstractSection.__init__(self, sectiontype, ...)
124 Node.__init__(self, ...)
125 self.sectiontype = sectiontype
126 end
127
128 function AbstractSection.option(self, class, ...)
129 if instanceof(class, AbstractValue) then
130 local obj = class(...)
131 obj.map = self.map
132 obj.config = self.config
133 self:append(obj)
134 return obj
135 else
136 error("class must be a descendent of AbstractValue")
137 end
138 end
139
140
141
142 --[[
143 NamedSection - A fixed configuration section defined by its name
144 ]]--
145 NamedSection = class(AbstractSection)
146
147 function NamedSection.__init__(self, section, ...)
148 AbstractSection.__init__(self, ...)
149 self.template = "cbi/nsection"
150
151 self.section = section
152 end
153
154 function NamedSection.option(self, ...)
155 local obj = AbstractSection.option(self, ...)
156 obj.section = self.section
157 return obj
158 end
159
160
161 --[[
162 TypedSection - A (set of) configuration section(s) defined by the type
163 addremove: Defines whether the user can add/remove sections of this type
164 anonymous: Allow creating anonymous sections
165 valid: a table with valid names or a function returning nil if invalid
166 ]]--
167 TypedSection = class(AbstractSection)
168
169 function TypedSection.__init__(self, ...)
170 AbstractSection.__init__(self, ...)
171 self.template = "cbi/tsection"
172
173 self.addremove = true
174 self.anonymous = false
175 self.valid = nil
176 end
177
178
179 --[[
180 AbstractValue - An abstract Value Type
181 null: Value can be empty
182 valid: A function returning the value if it is valid otherwise nil
183 depends: A table of option => value pairs of which one must be true
184 default: The default value
185 ]]--
186 AbstractValue = class(Node)
187
188 function AbstractValue.__init__(self, option, ...)
189 Node.__init__(self, ...)
190 self.option = option
191
192 self.valid = nil
193 self.depends = nil
194 self.default = nil
195 end
196
197
198 function AbstractValue.formvalue(self)
199 local key = "uci."..self.map.config.."."..self.section.."."..self.option
200 return ffluci.http.formvalue(key)
201 end
202
203 function AbstractValue.ucivalue(self)
204 return self.map:read()[self.section][self.option]
205 end
206
207 function AbstractValue.validate(self, value)
208 return ffluci.util.validate(value, nil, nil, self.valid)
209 end
210
211 function AbstractValue.write(self, value)
212 ffluci.model.uci.set(self.config, self.section, self.option, value)
213 end
214
215
216 --[[
217 Value - A one-line value
218 maxlength: The maximum length
219 isnumber: The value must be a valid (floating point) number
220 isinteger: The value must be a valid integer
221 ]]--
222 Value = class(AbstractValue)
223
224 function Value.__init__(self, ...)
225 AbstractValue.__init__(self, ...)
226 self.template = "cbi/value"
227
228 self.maxlength = nil
229 self.isnumber = false
230 self.isinteger = false
231 end
232
233
234 --[[
235 ListValue - A one-line value predefined in a list
236 ]]--
237 ListValue = class(AbstractValue)
238
239 function ListValue.__init__(self, ...)
240 AbstractValue.__init__(self, ...)
241 self.template = "cbi/lvalue"
242
243 self.list = {}
244 end
245
246 function ListValue.addValue(self, key, val)
247 val = val or key
248 self.list[key] = val
249 end