2008-03-16 18:11:22 +00:00
|
|
|
--[[
|
|
|
|
FFLuCI - Configuration Bind Interface
|
|
|
|
|
|
|
|
Description:
|
|
|
|
Offers an interface for binding confiugration values to certain
|
|
|
|
data types. Supports value and range validation and basic dependencies.
|
|
|
|
|
|
|
|
FileId:
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
License:
|
|
|
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
]]--
|
|
|
|
module("ffluci.cbi", package.seeall)
|
2008-03-21 19:30:53 +00:00
|
|
|
|
2008-03-17 21:38:03 +00:00
|
|
|
require("ffluci.template")
|
2008-03-16 18:11:22 +00:00
|
|
|
require("ffluci.util")
|
2008-03-20 20:33:43 +00:00
|
|
|
require("ffluci.http")
|
|
|
|
require("ffluci.model.uci")
|
2008-03-21 19:30:53 +00:00
|
|
|
|
|
|
|
local Template = ffluci.template.Template
|
|
|
|
local class = ffluci.util.class
|
2008-03-17 21:38:03 +00:00
|
|
|
local instanceof = ffluci.util.instanceof
|
2008-03-16 18:11:22 +00:00
|
|
|
|
|
|
|
|
2008-03-21 19:30:53 +00:00
|
|
|
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
|
|
|
|
|
2008-03-16 18:11:22 +00:00
|
|
|
-- Node pseudo abstract class
|
2008-03-16 20:13:11 +00:00
|
|
|
Node = class()
|
|
|
|
|
|
|
|
function Node.__init__(self, title, description)
|
|
|
|
self.children = {}
|
2008-03-21 19:30:53 +00:00
|
|
|
self.title = title or ""
|
|
|
|
self.description = description or ""
|
2008-03-17 21:38:03 +00:00
|
|
|
self.template = "cbi/node"
|
2008-03-16 18:11:22 +00:00
|
|
|
end
|
2008-03-16 20:13:11 +00:00
|
|
|
|
2008-03-16 18:11:22 +00:00
|
|
|
function Node.append(self, obj)
|
2008-03-16 20:13:11 +00:00
|
|
|
table.insert(self.children, obj)
|
2008-03-16 18:11:22 +00:00
|
|
|
end
|
|
|
|
|
2008-03-20 20:33:43 +00:00
|
|
|
function Node.parse(self)
|
|
|
|
for k, child in ipairs(self.children) do
|
|
|
|
child:parse()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-03-18 22:08:46 +00:00
|
|
|
function Node.render(self)
|
2008-03-21 19:30:53 +00:00
|
|
|
ffluci.template.render(self.template, {self=self})
|
2008-03-18 22:08:46 +00:00
|
|
|
end
|
|
|
|
|
2008-03-22 19:46:14 +00:00
|
|
|
function Node.render_children(self)
|
|
|
|
for k, node in ipairs(self.children) do
|
|
|
|
node:render()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-03-16 18:11:22 +00:00
|
|
|
|
2008-03-17 21:38:03 +00:00
|
|
|
--[[
|
|
|
|
Map - A map describing a configuration file
|
|
|
|
]]--
|
2008-03-16 20:13:11 +00:00
|
|
|
Map = class(Node)
|
|
|
|
|
2008-03-17 21:38:03 +00:00
|
|
|
function Map.__init__(self, config, ...)
|
|
|
|
Node.__init__(self, ...)
|
|
|
|
self.config = config
|
|
|
|
self.template = "cbi/map"
|
|
|
|
end
|
|
|
|
|
2008-03-22 21:26:44 +00:00
|
|
|
function Map.parse(self)
|
|
|
|
self.ucidata = ffluci.model.uci.show(self.config)
|
|
|
|
if not self.ucidata then
|
|
|
|
error("Unable to read UCI data: " .. self.config)
|
|
|
|
else
|
|
|
|
self.ucidata = self.ucidata[self.config]
|
|
|
|
end
|
|
|
|
Node.parse(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
function Map.render(self)
|
|
|
|
self.ucidata = ffluci.model.uci.show(self.config)
|
|
|
|
if not self.ucidata then
|
|
|
|
error("Unable to read UCI data: " .. self.config)
|
|
|
|
else
|
|
|
|
self.ucidata = self.ucidata[self.config]
|
|
|
|
end
|
|
|
|
Node.render(self)
|
|
|
|
end
|
|
|
|
|
2008-03-17 21:38:03 +00:00
|
|
|
function Map.section(self, class, ...)
|
2008-03-18 22:08:46 +00:00
|
|
|
if instanceof(class, AbstractSection) then
|
2008-03-20 20:33:43 +00:00
|
|
|
local obj = class(...)
|
|
|
|
obj.map = self
|
|
|
|
obj.config = self.config
|
|
|
|
self:append(obj)
|
2008-03-17 21:38:03 +00:00
|
|
|
return obj
|
|
|
|
else
|
|
|
|
error("class must be a descendent of AbstractSection")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
AbstractSection
|
|
|
|
]]--
|
|
|
|
AbstractSection = class(Node)
|
|
|
|
|
2008-03-18 22:08:46 +00:00
|
|
|
function AbstractSection.__init__(self, sectiontype, ...)
|
2008-03-17 21:38:03 +00:00
|
|
|
Node.__init__(self, ...)
|
2008-03-18 22:08:46 +00:00
|
|
|
self.sectiontype = sectiontype
|
2008-03-17 21:38:03 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function AbstractSection.option(self, class, ...)
|
|
|
|
if instanceof(class, AbstractValue) then
|
2008-03-20 20:33:43 +00:00
|
|
|
local obj = class(...)
|
|
|
|
obj.map = self.map
|
|
|
|
obj.config = self.config
|
|
|
|
self:append(obj)
|
2008-03-17 21:38:03 +00:00
|
|
|
return obj
|
|
|
|
else
|
|
|
|
error("class must be a descendent of AbstractValue")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
NamedSection - A fixed configuration section defined by its name
|
|
|
|
]]--
|
|
|
|
NamedSection = class(AbstractSection)
|
|
|
|
|
|
|
|
function NamedSection.__init__(self, section, ...)
|
|
|
|
AbstractSection.__init__(self, ...)
|
|
|
|
self.template = "cbi/nsection"
|
2008-03-18 22:08:46 +00:00
|
|
|
|
|
|
|
self.section = section
|
|
|
|
end
|
|
|
|
|
|
|
|
function NamedSection.option(self, ...)
|
|
|
|
local obj = AbstractSection.option(self, ...)
|
|
|
|
obj.section = self.section
|
|
|
|
return obj
|
2008-03-17 21:38:03 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
TypedSection - A (set of) configuration section(s) defined by the type
|
|
|
|
addremove: Defines whether the user can add/remove sections of this type
|
|
|
|
anonymous: Allow creating anonymous sections
|
|
|
|
valid: a table with valid names or a function returning nil if invalid
|
|
|
|
]]--
|
|
|
|
TypedSection = class(AbstractSection)
|
|
|
|
|
2008-03-18 22:08:46 +00:00
|
|
|
function TypedSection.__init__(self, ...)
|
2008-03-17 21:38:03 +00:00
|
|
|
AbstractSection.__init__(self, ...)
|
|
|
|
self.template = "cbi/tsection"
|
|
|
|
|
|
|
|
self.addremove = true
|
|
|
|
self.anonymous = false
|
|
|
|
self.valid = nil
|
|
|
|
end
|
|
|
|
|
2008-03-22 21:26:44 +00:00
|
|
|
function TypedSection.parse(self)
|
|
|
|
for k, v in pairs(self:ucisections()) do
|
|
|
|
for i, node in ipairs(self.children) do
|
|
|
|
node.section = k
|
|
|
|
node:parse()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-03-22 19:46:14 +00:00
|
|
|
function TypedSection.render_children(self, section)
|
|
|
|
for k, node in ipairs(self.children) do
|
|
|
|
node.section = section
|
|
|
|
node:render()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function TypedSection.ucisections(self)
|
|
|
|
local sections = {}
|
2008-03-22 21:26:44 +00:00
|
|
|
for k, v in pairs(self.map.ucidata) do
|
2008-03-22 19:46:14 +00:00
|
|
|
if v[".type"] == self.sectiontype then
|
|
|
|
sections[k] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return sections
|
|
|
|
end
|
|
|
|
|
2008-03-17 21:38:03 +00:00
|
|
|
|
|
|
|
--[[
|
|
|
|
AbstractValue - An abstract Value Type
|
|
|
|
null: Value can be empty
|
2008-03-20 20:33:43 +00:00
|
|
|
valid: A function returning the value if it is valid otherwise nil
|
2008-03-17 21:38:03 +00:00
|
|
|
depends: A table of option => value pairs of which one must be true
|
2008-03-18 22:08:46 +00:00
|
|
|
default: The default value
|
2008-03-17 21:38:03 +00:00
|
|
|
]]--
|
|
|
|
AbstractValue = class(Node)
|
|
|
|
|
|
|
|
function AbstractValue.__init__(self, option, ...)
|
2008-03-16 20:13:11 +00:00
|
|
|
Node.__init__(self, ...)
|
2008-03-17 21:38:03 +00:00
|
|
|
self.option = option
|
|
|
|
|
|
|
|
self.valid = nil
|
|
|
|
self.depends = nil
|
2008-03-18 22:08:46 +00:00
|
|
|
self.default = nil
|
2008-03-16 20:13:11 +00:00
|
|
|
end
|
2008-03-20 20:33:43 +00:00
|
|
|
|
|
|
|
function AbstractValue.formvalue(self)
|
2008-03-22 21:26:44 +00:00
|
|
|
local key = "cbid."..self.map.config.."."..self.section.."."..self.option
|
2008-03-20 20:33:43 +00:00
|
|
|
return ffluci.http.formvalue(key)
|
|
|
|
end
|
|
|
|
|
2008-03-22 21:26:44 +00:00
|
|
|
function AbstractValue.parse(self)
|
|
|
|
local fvalue = self:validate(self:formvalue())
|
|
|
|
if fvalue and not (fvalue == self:ucivalue()) then
|
|
|
|
self:write(fvalue)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-03-20 20:33:43 +00:00
|
|
|
function AbstractValue.ucivalue(self)
|
2008-03-22 21:26:44 +00:00
|
|
|
return self.map.ucidata[self.section][self.option]
|
2008-03-20 20:33:43 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function AbstractValue.validate(self, value)
|
|
|
|
return ffluci.util.validate(value, nil, nil, self.valid)
|
|
|
|
end
|
|
|
|
|
|
|
|
function AbstractValue.write(self, value)
|
2008-03-22 21:26:44 +00:00
|
|
|
return ffluci.model.uci.set(self.config, self.section, self.option, value)
|
2008-03-20 20:33:43 +00:00
|
|
|
end
|
|
|
|
|
2008-03-17 21:38:03 +00:00
|
|
|
|
|
|
|
--[[
|
2008-03-18 22:08:46 +00:00
|
|
|
Value - A one-line value
|
2008-03-17 21:38:03 +00:00
|
|
|
maxlength: The maximum length
|
|
|
|
isnumber: The value must be a valid (floating point) number
|
|
|
|
isinteger: The value must be a valid integer
|
|
|
|
]]--
|
|
|
|
Value = class(AbstractValue)
|
2008-03-16 20:13:11 +00:00
|
|
|
|
2008-03-17 21:38:03 +00:00
|
|
|
function Value.__init__(self, ...)
|
|
|
|
AbstractValue.__init__(self, ...)
|
|
|
|
self.template = "cbi/value"
|
|
|
|
|
|
|
|
self.maxlength = nil
|
|
|
|
self.isnumber = false
|
|
|
|
self.isinteger = false
|
2008-03-16 20:13:11 +00:00
|
|
|
end
|
2008-03-17 21:38:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
--[[
|
2008-03-18 22:08:46 +00:00
|
|
|
ListValue - A one-line value predefined in a list
|
2008-03-17 21:38:03 +00:00
|
|
|
]]--
|
2008-03-18 22:08:46 +00:00
|
|
|
ListValue = class(AbstractValue)
|
2008-03-17 21:38:03 +00:00
|
|
|
|
2008-03-18 22:08:46 +00:00
|
|
|
function ListValue.__init__(self, ...)
|
2008-03-17 21:38:03 +00:00
|
|
|
AbstractValue.__init__(self, ...)
|
2008-03-20 20:33:43 +00:00
|
|
|
self.template = "cbi/lvalue"
|
2008-03-18 22:08:46 +00:00
|
|
|
|
|
|
|
self.list = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
function ListValue.addValue(self, key, val)
|
|
|
|
val = val or key
|
|
|
|
self.list[key] = val
|
2008-03-17 21:38:03 +00:00
|
|
|
end
|