2 LuCI - Configuration Bind Interface
5 Offers an interface for binding configuration 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("luci.cbi", package.seeall)
29 require("luci.template")
35 --local event = require "luci.sys.event"
36 local uci = require("luci.model.uci")
37 local class = luci.util.class
38 local instanceof = luci.util.instanceof
48 CREATE_PREFIX = "cbi.cts."
49 REMOVE_PREFIX = "cbi.rts."
51 -- Loads a CBI map from given file, creating an environment and returns it
52 function load(cbimap, ...)
54 local i18n = require "luci.i18n"
55 require("luci.config")
58 local upldir = "/lib/uci/upload/"
59 local cbidir = luci.util.libpath() .. "/model/cbi/"
61 assert(luci.fs.stat(cbimap) or luci.fs.stat(cbidir..cbimap..".lua"),
64 local func, err = loadfile(cbimap)
66 func, err = loadfile(cbidir..cbimap..".lua")
70 luci.i18n.loadc("cbi")
71 luci.i18n.loadc("uvl")
74 translate=i18n.translate,
75 translatef=i18n.translatef,
79 setfenv(func, setmetatable(env, {__index =
81 return rawget(tbl, key) or _M[key] or _G[key]
84 local maps = { func() }
86 local has_upload = false
88 for i, map in ipairs(maps) do
89 if not instanceof(map, Node) then
90 error("CBI map returns no valid map object!")
94 if map.upload_fields then
96 for _, field in ipairs(map.upload_fields) do
98 field.config .. '.' ..
99 field.section.sectiontype .. '.' ..
108 local uci = luci.model.uci.cursor()
109 local prm = luci.http.context.request.message.params
112 luci.http.setfilehandler(
113 function( field, chunk, eof )
114 if not field then return end
115 if field.name and not cbid then
116 local c, s, o = field.name:gmatch(
117 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
120 if c and s and o then
121 local t = uci:get( c, s )
122 if t and uploads[c.."."..t.."."..o] then
123 local path = upldir .. field.name
124 fd = io.open(path, "w")
133 if field.name == cbid and fd then
149 local function _uvl_validate_section(node, name)
150 local co = node.map:get()
152 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
153 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
155 local function tag_fields(e)
156 if e.option and node.fields[e.option] then
157 if node.fields[e.option].error then
158 node.fields[e.option].error[name] = e
160 node.fields[e.option].error = { [name] = e }
163 for _, c in ipairs(e.childs) do tag_fields(c) end
167 local function tag_section(e)
169 for _, c in ipairs(e.childs) do
170 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
171 table.insert( s, c.childs[1]:string() )
173 table.insert( s, c:string() )
180 node.error = { [name] = s }
185 local stat, err = node.map.validator:validate_section(node.config, name, co)
187 node.map.save = false
194 local function _uvl_strip_remote_dependencies(deps)
197 for k, v in pairs(deps) do
198 k = k:gsub("%$config%.%$section%.", "")
199 if k:match("^[%w_]+$") and type(v) == "string" then
208 -- Node pseudo abstract class
211 function Node.__init__(self, title, description)
213 self.title = title or ""
214 self.description = description or ""
215 self.template = "cbi/node"
219 function Node._i18n(self, config, section, option, title, description)
222 if type(luci.i18n) == "table" then
224 local key = config and config:gsub("[^%w]+", "") or ""
226 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
227 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
229 self.title = title or luci.i18n.translate( key, option or section or config )
230 self.description = description or luci.i18n.translate( key .. "_desc", "" )
235 function Node.prepare(self, ...)
236 for k, child in ipairs(self.children) do
241 -- Append child nodes
242 function Node.append(self, obj)
243 table.insert(self.children, obj)
246 -- Parse this node and its children
247 function Node.parse(self, ...)
248 for k, child in ipairs(self.children) do
254 function Node.render(self, scope)
258 luci.template.render(self.template, scope)
261 -- Render the children
262 function Node.render_children(self, ...)
263 for k, node in ipairs(self.children) do
270 A simple template element
272 Template = class(Node)
274 function Template.__init__(self, template)
276 self.template = template
279 function Template.render(self)
280 luci.template.render(self.template, {self=self})
285 Map - A map describing a configuration file
289 function Map.__init__(self, config, ...)
290 Node.__init__(self, ...)
291 Node._i18n(self, config, nil, nil, ...)
294 self.parsechain = {self.config}
295 self.template = "cbi/map"
296 self.apply_on_parse = nil
297 self.readinput = true
299 self.uci = uci.cursor()
304 if not self.uci:load(self.config) then
305 error("Unable to read UCI data: " .. self.config)
308 self.validator = luci.uvl.UVL()
309 self.scheme = self.validator:get_scheme(self.config)
313 function Map.formvalue(self, key)
314 return self.readinput and luci.http.formvalue(key)
317 function Map.formvaluetable(self, key)
318 return self.readinput and luci.http.formvaluetable(key)
321 function Map.get_scheme(self, sectiontype, option)
323 return self.scheme and self.scheme.sections[sectiontype]
325 return self.scheme and self.scheme.variables[sectiontype]
326 and self.scheme.variables[sectiontype][option]
330 function Map.submitstate(self)
331 return self:formvalue("cbi.submit")
334 -- Chain foreign config
335 function Map.chain(self, config)
336 table.insert(self.parsechain, config)
339 function Map.state_handler(self, state)
343 -- Use optimized UCI writing
344 function Map.parse(self, readinput, ...)
345 self.readinput = (readinput ~= false)
346 Node.parse(self, ...)
349 for i, config in ipairs(self.parsechain) do
350 self.uci:save(config)
352 if self:submitstate() and (self.autoapply or luci.http.formvalue("cbi.apply")) then
353 for i, config in ipairs(self.parsechain) do
354 self.uci:commit(config)
356 -- Refresh data because commit changes section names
357 self.uci:load(config)
359 if self.apply_on_parse then
360 self.uci:apply(self.parsechain)
362 self._apply = function()
363 local cmd = self.uci:apply(self.parsechain, true)
369 Node.parse(self, true)
372 for i, config in ipairs(self.parsechain) do
373 self.uci:unload(config)
375 if type(self.commit_handler) == "function" then
376 self:commit_handler(self:submitstate())
380 if self:submitstate() then
382 self.state = self.changed and FORM_CHANGED or FORM_VALID
384 self.state = FORM_INVALID
387 self.state = FORM_NODATA
390 return self:state_handler(self.state)
393 function Map.render(self, ...)
394 Node.render(self, ...)
396 local fp = self._apply()
402 -- Creates a child section
403 function Map.section(self, class, ...)
404 if instanceof(class, AbstractSection) then
405 local obj = class(self, ...)
409 error("class must be a descendent of AbstractSection")
414 function Map.add(self, sectiontype)
415 return self.uci:add(self.config, sectiontype)
419 function Map.set(self, section, option, value)
421 return self.uci:set(self.config, section, option, value)
423 return self.uci:set(self.config, section, value)
428 function Map.del(self, section, option)
430 return self.uci:delete(self.config, section, option)
432 return self.uci:delete(self.config, section)
437 function Map.get(self, section, option)
439 return self.uci:get_all(self.config)
441 return self.uci:get(self.config, section, option)
443 return self.uci:get_all(self.config, section)
450 Compound = class(Node)
452 function Compound.__init__(self, ...)
454 self.children = {...}
457 function Compound.parse(self, ...)
458 local cstate, state = 0, 0
460 for k, child in ipairs(self.children) do
461 cstate = child:parse(...)
462 state = (not state or cstate < state) and cstate or state
470 Delegator - Node controller
472 Delegator = class(Node)
473 function Delegator.__init__(self, ...)
474 Node.__init__(self, ...)
476 self.template = "cbi/delegator"
479 function Delegator.state(self, name, node, transitor)
480 transitor = transitor or self.transistor_linear
481 local state = {node=node, name=name, transitor=transitor}
483 assert(instanceof(node, Node), "Invalid node")
484 assert(not self.nodes[name], "Duplicate entry")
486 self.nodes[name] = state
492 function Delegator.get(self, name)
493 return self.nodes[name]
496 function Delegator.transistor_linear(self, state, cstate)
498 for i, child in ipairs(self.children) do
499 if state == child then
500 return self.children[i+1]
508 function Delegator.parse(self, ...)
509 local active = self:getactive()
510 assert(active, "Invalid state")
512 local cstate = active.node:parse()
513 self.active = active.transistor(self, active.node, cstate)
515 if not self.active then
518 self.active:parse(false)
523 function Delegator.render(self, ...)
524 self.active.node:render(...)
527 function Delegator.getactive(self)
528 return self:get(Map.formvalue(self, "cbi.delegated")
529 or (self.children[1] and self.children[1].name))
537 Page.__init__ = Node.__init__
538 Page.parse = function() end
542 SimpleForm - A Simple non-UCI form
544 SimpleForm = class(Node)
546 function SimpleForm.__init__(self, config, title, description, data)
547 Node.__init__(self, title, description)
549 self.data = data or {}
550 self.template = "cbi/simpleform"
552 self.pageaction = false
553 self.readinput = true
556 SimpleForm.formvalue = Map.formvalue
557 SimpleForm.formvaluetable = Map.formvaluetable
559 function SimpleForm.parse(self, readinput, ...)
560 self.readinput = (readinput ~= false)
561 if self:submitstate() then
562 Node.parse(self, 1, ...)
566 for k, j in ipairs(self.children) do
567 for i, v in ipairs(j.children) do
569 and (not v.tag_missing or not v.tag_missing[1])
570 and (not v.tag_invalid or not v.tag_invalid[1])
575 not self:submitstate() and FORM_NODATA
576 or valid and FORM_VALID
579 self.dorender = not self.handle or self:handle(state, self.data) ~= false
583 function SimpleForm.render(self, ...)
584 if self.dorender then
585 Node.render(self, ...)
589 function SimpleForm.submitstate(self)
590 return self:formvalue("cbi.submit")
593 function SimpleForm.section(self, class, ...)
594 if instanceof(class, AbstractSection) then
595 local obj = class(self, ...)
599 error("class must be a descendent of AbstractSection")
603 -- Creates a child field
604 function SimpleForm.field(self, class, ...)
606 for k, v in ipairs(self.children) do
607 if instanceof(v, SimpleSection) then
613 section = self:section(SimpleSection)
616 if instanceof(class, AbstractValue) then
617 local obj = class(self, section, ...)
618 obj.track_missing = true
622 error("class must be a descendent of AbstractValue")
626 function SimpleForm.set(self, section, option, value)
627 self.data[option] = value
631 function SimpleForm.del(self, section, option)
632 self.data[option] = nil
636 function SimpleForm.get(self, section, option)
637 return self.data[option]
641 function SimpleForm.get_scheme()
650 AbstractSection = class(Node)
652 function AbstractSection.__init__(self, map, sectiontype, ...)
653 Node.__init__(self, ...)
654 self.sectiontype = sectiontype
656 self.config = map.config
661 self.tag_invalid = {}
662 self.tag_deperror = {}
666 self.addremove = false
670 -- Appends a new option
671 function AbstractSection.option(self, class, option, ...)
672 -- Autodetect from UVL
673 if class == true and self.map:get_scheme(self.sectiontype, option) then
674 local vs = self.map:get_scheme(self.sectiontype, option)
675 if vs.type == "boolean" then
677 elseif vs.type == "list" then
679 elseif vs.type == "enum" or vs.type == "reference" then
686 if instanceof(class, AbstractValue) then
687 local obj = class(self.map, self, option, ...)
689 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
692 self.fields[option] = obj
694 elseif class == true then
695 error("No valid class was given and autodetection failed.")
697 error("class must be a descendant of AbstractValue")
701 -- Parse optional options
702 function AbstractSection.parse_optionals(self, section)
703 if not self.optional then
707 self.optionals[section] = {}
709 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
710 for k,v in ipairs(self.children) do
711 if v.optional and not v:cfgvalue(section) then
712 if field == v.option then
715 table.insert(self.optionals[section], v)
720 if field and #field > 0 and self.dynamic then
721 self:add_dynamic(field)
725 -- Add a dynamic option
726 function AbstractSection.add_dynamic(self, field, optional)
727 local o = self:option(Value, field, field)
728 o.optional = optional
731 -- Parse all dynamic options
732 function AbstractSection.parse_dynamic(self, section)
733 if not self.dynamic then
737 local arr = luci.util.clone(self:cfgvalue(section))
738 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
739 for k, v in pairs(form) do
743 for key,val in pairs(arr) do
746 for i,c in ipairs(self.children) do
747 if c.option == key then
752 if create and key:sub(1, 1) ~= "." then
753 self:add_dynamic(key, true)
758 -- Returns the section's UCI table
759 function AbstractSection.cfgvalue(self, section)
760 return self.map:get(section)
764 function AbstractSection.push_events(self)
765 --luci.util.append(self.map.events, self.events)
766 self.map.changed = true
769 -- Removes the section
770 function AbstractSection.remove(self, section)
771 return self.map:del(section)
774 -- Creates the section
775 function AbstractSection.create(self, section)
779 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
781 section = self.map:add(self.sectiontype)
786 for k,v in pairs(self.children) do
788 self.map:set(section, v.option, v.default)
792 for k,v in pairs(self.defaults) do
793 self.map:set(section, k, v)
801 SimpleSection = class(AbstractSection)
803 function SimpleSection.__init__(self, form, ...)
804 AbstractSection.__init__(self, form, nil, ...)
805 self.template = "cbi/nullsection"
809 Table = class(AbstractSection)
811 function Table.__init__(self, form, data, ...)
812 local datasource = {}
813 datasource.config = "table"
816 datasource.formvalue = Map.formvalue
817 datasource.formvaluetable = Map.formvaluetable
818 datasource.readinput = true
820 function datasource.get(self, section, option)
821 return data[section] and data[section][option]
824 function datasource.submitstate(self)
825 return Map.formvalue(self, "cbi.submit")
828 function datasource.del(...)
832 function datasource.get_scheme()
836 AbstractSection.__init__(self, datasource, "table", ...)
837 self.template = "cbi/tblsection"
838 self.rowcolors = true
839 self.anonymous = true
842 function Table.parse(self, readinput)
843 self.map.readinput = (readinput ~= false)
844 for i, k in ipairs(self:cfgsections()) do
845 if self.map:submitstate() then
851 function Table.cfgsections(self)
854 for i, v in luci.util.kspairs(self.data) do
855 table.insert(sections, i)
864 NamedSection - A fixed configuration section defined by its name
866 NamedSection = class(AbstractSection)
868 function NamedSection.__init__(self, map, section, stype, ...)
869 AbstractSection.__init__(self, map, stype, ...)
870 Node._i18n(self, map.config, section, nil, ...)
873 self.addremove = false
875 -- Use defaults from UVL
876 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
877 local vs = self.map:get_scheme(self.sectiontype)
878 self.addremove = not vs.unique and not vs.required
879 self.dynamic = vs.dynamic
880 self.title = self.title or vs.title
881 self.description = self.description or vs.descr
884 self.template = "cbi/nsection"
885 self.section = section
888 function NamedSection.parse(self, novld)
889 local s = self.section
890 local active = self:cfgvalue(s)
892 if self.addremove then
893 local path = self.config.."."..s
894 if active then -- Remove the section
895 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
899 else -- Create and apply default values
900 if self.map:formvalue("cbi.cns."..path) then
908 AbstractSection.parse_dynamic(self, s)
909 if self.map:submitstate() then
912 if not novld and not self.override_scheme and self.map.scheme then
913 _uvl_validate_section(self, s)
916 AbstractSection.parse_optionals(self, s)
926 TypedSection - A (set of) configuration section(s) defined by the type
927 addremove: Defines whether the user can add/remove sections of this type
928 anonymous: Allow creating anonymous sections
929 validate: a validation function returning nil if the section is invalid
931 TypedSection = class(AbstractSection)
933 function TypedSection.__init__(self, map, type, ...)
934 AbstractSection.__init__(self, map, type, ...)
935 Node._i18n(self, map.config, type, nil, ...)
937 self.template = "cbi/tsection"
939 self.anonymous = false
941 -- Use defaults from UVL
942 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
943 local vs = self.map:get_scheme(self.sectiontype)
944 self.addremove = not vs.unique and not vs.required
945 self.dynamic = vs.dynamic
946 self.anonymous = not vs.named
947 self.title = self.title or vs.title
948 self.description = self.description or vs.descr
952 -- Return all matching UCI sections for this TypedSection
953 function TypedSection.cfgsections(self)
955 self.map.uci:foreach(self.map.config, self.sectiontype,
957 if self:checkscope(section[".name"]) then
958 table.insert(sections, section[".name"])
965 -- Limits scope to sections that have certain option => value pairs
966 function TypedSection.depends(self, option, value)
967 table.insert(self.deps, {option=option, value=value})
970 function TypedSection.parse(self, novld)
971 if self.addremove then
973 local crval = REMOVE_PREFIX .. self.config
974 local name = self.map:formvaluetable(crval)
975 for k,v in pairs(name) do
976 if k:sub(-2) == ".x" then
979 if self:cfgvalue(k) and self:checkscope(k) then
986 for i, k in ipairs(self:cfgsections()) do
987 AbstractSection.parse_dynamic(self, k)
988 if self.map:submitstate() then
991 if not novld and not self.override_scheme and self.map.scheme then
992 _uvl_validate_section(self, k)
995 AbstractSection.parse_optionals(self, k)
998 if self.addremove then
1001 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1002 local name = self.map:formvalue(crval)
1003 if self.anonymous then
1005 created = self:create()
1009 -- Ignore if it already exists
1010 if self:cfgvalue(name) then
1014 name = self:checkscope(name)
1017 self.err_invalid = true
1020 if name and #name > 0 then
1021 created = self:create(name) and name
1023 self.invalid_cts = true
1030 AbstractSection.parse_optionals(self, created)
1034 if created or self.changed then
1039 -- Verifies scope of sections
1040 function TypedSection.checkscope(self, section)
1041 -- Check if we are not excluded
1042 if self.filter and not self:filter(section) then
1046 -- Check if at least one dependency is met
1047 if #self.deps > 0 and self:cfgvalue(section) then
1050 for k, v in ipairs(self.deps) do
1051 if self:cfgvalue(section)[v.option] == v.value then
1061 return self:validate(section)
1065 -- Dummy validate function
1066 function TypedSection.validate(self, section)
1072 AbstractValue - An abstract Value Type
1073 null: Value can be empty
1074 valid: A function returning the value if it is valid otherwise nil
1075 depends: A table of option => value pairs of which one must be true
1076 default: The default value
1077 size: The size of the input fields
1078 rmempty: Unset value if empty
1079 optional: This value is optional (see AbstractSection.optionals)
1081 AbstractValue = class(Node)
1083 function AbstractValue.__init__(self, map, section, option, ...)
1084 Node.__init__(self, ...)
1085 self.section = section
1086 self.option = option
1088 self.config = map.config
1089 self.tag_invalid = {}
1090 self.tag_missing = {}
1091 self.tag_reqerror = {}
1094 --self.cast = "string"
1096 self.track_missing = false
1097 --self.rmempty = false
1100 self.optional = false
1103 function AbstractValue.prepare(self)
1104 -- Use defaults from UVL
1105 if not self.override_scheme
1106 and self.map:get_scheme(self.section.sectiontype, self.option) then
1107 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1108 if self.rmempty == nil then
1109 self.rmempty = not vs.required
1111 if self.cast == nil then
1112 self.cast = (vs.type == "list") and "list" or "string"
1114 self.title = self.title or vs.title
1115 self.description = self.description or vs.descr
1116 if self.default == nil then
1117 self.default = vs.default
1120 if vs.depends and not self.override_dependencies then
1121 for i, deps in ipairs(vs.depends) do
1122 deps = _uvl_strip_remote_dependencies(deps)
1130 self.cast = self.cast or "string"
1133 -- Add a dependencie to another section field
1134 function AbstractValue.depends(self, field, value)
1136 if type(field) == "string" then
1143 table.insert(self.deps, {deps=deps, add=""})
1146 -- Generates the unique CBID
1147 function AbstractValue.cbid(self, section)
1148 return "cbid."..self.map.config.."."..section.."."..self.option
1151 -- Return whether this object should be created
1152 function AbstractValue.formcreated(self, section)
1153 local key = "cbi.opt."..self.config.."."..section
1154 return (self.map:formvalue(key) == self.option)
1157 -- Returns the formvalue for this object
1158 function AbstractValue.formvalue(self, section)
1159 return self.map:formvalue(self:cbid(section))
1162 function AbstractValue.additional(self, value)
1163 self.optional = value
1166 function AbstractValue.mandatory(self, value)
1167 self.rmempty = not value
1170 function AbstractValue.parse(self, section)
1171 local fvalue = self:formvalue(section)
1172 local cvalue = self:cfgvalue(section)
1174 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1175 fvalue = self:transform(self:validate(fvalue, section))
1178 self.error[section] = "invalid"
1180 self.error = { [section] = "invalid" }
1182 self.map.save = false
1184 if fvalue and not (fvalue == cvalue) then
1185 if self:write(section, fvalue) then
1187 self.section.changed = true
1188 --luci.util.append(self.map.events, self.events)
1191 else -- Unset the UCI or error
1192 if self.rmempty or self.optional then
1193 if self:remove(section) then
1195 self.section.changed = true
1196 --luci.util.append(self.map.events, self.events)
1198 elseif cvalue ~= fvalue then
1199 self:write(section, fvalue)
1201 self.error[section] = "missing"
1203 self.error = { [section] = "missing" }
1205 self.map.save = false
1210 -- Render if this value exists or if it is mandatory
1211 function AbstractValue.render(self, s, scope)
1212 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1215 scope.cbid = self:cbid(s)
1216 scope.striptags = luci.util.striptags
1218 scope.ifattr = function(cond,key,val)
1220 return string.format(
1221 ' %s="%s"', tostring(key),
1222 luci.util.pcdata(tostring( val
1224 or (type(self[key]) ~= "function" and self[key])
1232 scope.attr = function(...)
1233 return scope.ifattr( true, ... )
1236 Node.render(self, scope)
1240 -- Return the UCI value of this object
1241 function AbstractValue.cfgvalue(self, section)
1242 local value = self.map:get(section, self.option)
1245 elseif not self.cast or self.cast == type(value) then
1247 elseif self.cast == "string" then
1248 if type(value) == "table" then
1251 elseif self.cast == "table" then
1252 return luci.util.split(value, "%s+", nil, true)
1256 -- Validate the form value
1257 function AbstractValue.validate(self, value)
1261 AbstractValue.transform = AbstractValue.validate
1265 function AbstractValue.write(self, section, value)
1266 return self.map:set(section, self.option, value)
1270 function AbstractValue.remove(self, section)
1271 return self.map:del(section, self.option)
1278 Value - A one-line value
1279 maxlength: The maximum length
1281 Value = class(AbstractValue)
1283 function Value.__init__(self, ...)
1284 AbstractValue.__init__(self, ...)
1285 self.template = "cbi/value"
1290 function Value.value(self, key, val)
1292 table.insert(self.keylist, tostring(key))
1293 table.insert(self.vallist, tostring(val))
1297 -- DummyValue - This does nothing except being there
1298 DummyValue = class(AbstractValue)
1300 function DummyValue.__init__(self, ...)
1301 AbstractValue.__init__(self, ...)
1302 self.template = "cbi/dvalue"
1306 function DummyValue.cfgvalue(self, section)
1309 if type(self.value) == "function" then
1310 value = self:value(section)
1315 value = AbstractValue.cfgvalue(self, section)
1320 function DummyValue.parse(self)
1326 Flag - A flag being enabled or disabled
1328 Flag = class(AbstractValue)
1330 function Flag.__init__(self, ...)
1331 AbstractValue.__init__(self, ...)
1332 self.template = "cbi/fvalue"
1338 -- A flag can only have two states: set or unset
1339 function Flag.parse(self, section)
1340 local fvalue = self:formvalue(section)
1343 fvalue = self.enabled
1345 fvalue = self.disabled
1348 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1349 if not(fvalue == self:cfgvalue(section)) then
1350 self:write(section, fvalue)
1353 self:remove(section)
1360 ListValue - A one-line value predefined in a list
1361 widget: The widget that will be used (select, radio)
1363 ListValue = class(AbstractValue)
1365 function ListValue.__init__(self, ...)
1366 AbstractValue.__init__(self, ...)
1367 self.template = "cbi/lvalue"
1372 self.widget = "select"
1375 function ListValue.prepare(self, ...)
1376 AbstractValue.prepare(self, ...)
1377 if not self.override_scheme
1378 and self.map:get_scheme(self.section.sectiontype, self.option) then
1379 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1380 if self.value and vs.valuelist and not self.override_values then
1381 for k, v in ipairs(vs.valuelist) do
1383 if not self.override_dependencies
1384 and vs.enum_depends and vs.enum_depends[v.value] then
1385 for i, dep in ipairs(vs.enum_depends[v.value]) do
1386 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1389 self:value(v.value, v.title or v.value, unpack(deps))
1395 function ListValue.value(self, key, val, ...)
1396 if luci.util.contains(self.keylist, key) then
1401 table.insert(self.keylist, tostring(key))
1402 table.insert(self.vallist, tostring(val))
1404 for i, deps in ipairs({...}) do
1405 table.insert(self.deps, {add = "-"..key, deps=deps})
1409 function ListValue.validate(self, val)
1410 if luci.util.contains(self.keylist, val) then
1420 MultiValue - Multiple delimited values
1421 widget: The widget that will be used (select, checkbox)
1422 delimiter: The delimiter that will separate the values (default: " ")
1424 MultiValue = class(AbstractValue)
1426 function MultiValue.__init__(self, ...)
1427 AbstractValue.__init__(self, ...)
1428 self.template = "cbi/mvalue"
1433 self.widget = "checkbox"
1434 self.delimiter = " "
1437 function MultiValue.render(self, ...)
1438 if self.widget == "select" and not self.size then
1439 self.size = #self.vallist
1442 AbstractValue.render(self, ...)
1445 function MultiValue.value(self, key, val)
1446 if luci.util.contains(self.keylist, key) then
1451 table.insert(self.keylist, tostring(key))
1452 table.insert(self.vallist, tostring(val))
1455 function MultiValue.valuelist(self, section)
1456 local val = self:cfgvalue(section)
1458 if not(type(val) == "string") then
1462 return luci.util.split(val, self.delimiter)
1465 function MultiValue.validate(self, val)
1466 val = (type(val) == "table") and val or {val}
1470 for i, value in ipairs(val) do
1471 if luci.util.contains(self.keylist, value) then
1472 result = result and (result .. self.delimiter .. value) or value
1480 StaticList = class(MultiValue)
1482 function StaticList.__init__(self, ...)
1483 MultiValue.__init__(self, ...)
1485 self.valuelist = self.cfgvalue
1487 if not self.override_scheme
1488 and self.map:get_scheme(self.section.sectiontype, self.option) then
1489 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1490 if self.value and vs.values and not self.override_values then
1491 for k, v in pairs(vs.values) do
1498 function StaticList.validate(self, value)
1499 value = (type(value) == "table") and value or {value}
1502 for i, v in ipairs(value) do
1503 if luci.util.contains(self.vallist, v) then
1504 table.insert(valid, v)
1511 DynamicList = class(AbstractValue)
1513 function DynamicList.__init__(self, ...)
1514 AbstractValue.__init__(self, ...)
1515 self.template = "cbi/dynlist"
1521 function DynamicList.value(self, key, val)
1523 table.insert(self.keylist, tostring(key))
1524 table.insert(self.vallist, tostring(val))
1527 function DynamicList.formvalue(self, section)
1528 local value = AbstractValue.formvalue(self, section)
1529 value = (type(value) == "table") and value or {value}
1532 for i, v in ipairs(value) do
1534 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1535 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1536 table.insert(valid, v)
1545 TextValue - A multi-line value
1548 TextValue = class(AbstractValue)
1550 function TextValue.__init__(self, ...)
1551 AbstractValue.__init__(self, ...)
1552 self.template = "cbi/tvalue"
1558 Button = class(AbstractValue)
1560 function Button.__init__(self, ...)
1561 AbstractValue.__init__(self, ...)
1562 self.template = "cbi/button"
1563 self.inputstyle = nil
1568 FileUpload = class(AbstractValue)
1570 function FileUpload.__init__(self, ...)
1571 AbstractValue.__init__(self, ...)
1572 self.template = "cbi/upload"
1573 if not self.map.upload_fields then
1574 self.map.upload_fields = { self }
1576 self.map.upload_fields[#self.map.upload_fields+1] = self
1580 function FileUpload.formcreated(self, section)
1581 return AbstractValue.formcreated(self, section) or
1582 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1583 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1586 function FileUpload.cfgvalue(self, section)
1587 local val = AbstractValue.cfgvalue(self, section)
1588 if val and luci.fs.access(val) then
1594 function FileUpload.formvalue(self, section)
1595 local val = AbstractValue.formvalue(self, section)
1597 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1598 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1608 function FileUpload.remove(self, section)
1609 local val = AbstractValue.formvalue(self, section)
1610 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1611 return AbstractValue.remove(self, section)
1615 FileBrowser = class(AbstractValue)
1617 function FileBrowser.__init__(self, ...)
1618 AbstractValue.__init__(self, ...)
1619 self.template = "cbi/browser"