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])
576 not self:submitstate() and FORM_NODATA
577 or valid and FORM_VALID
580 self.dorender = not self.handle or self:handle(state, self.data) ~= false
584 function SimpleForm.render(self, ...)
585 if self.dorender then
586 Node.render(self, ...)
590 function SimpleForm.submitstate(self)
591 return self:formvalue("cbi.submit")
594 function SimpleForm.section(self, class, ...)
595 if instanceof(class, AbstractSection) then
596 local obj = class(self, ...)
600 error("class must be a descendent of AbstractSection")
604 -- Creates a child field
605 function SimpleForm.field(self, class, ...)
607 for k, v in ipairs(self.children) do
608 if instanceof(v, SimpleSection) then
614 section = self:section(SimpleSection)
617 if instanceof(class, AbstractValue) then
618 local obj = class(self, section, ...)
619 obj.track_missing = true
623 error("class must be a descendent of AbstractValue")
627 function SimpleForm.set(self, section, option, value)
628 self.data[option] = value
632 function SimpleForm.del(self, section, option)
633 self.data[option] = nil
637 function SimpleForm.get(self, section, option)
638 return self.data[option]
642 function SimpleForm.get_scheme()
651 AbstractSection = class(Node)
653 function AbstractSection.__init__(self, map, sectiontype, ...)
654 Node.__init__(self, ...)
655 self.sectiontype = sectiontype
657 self.config = map.config
662 self.tag_invalid = {}
663 self.tag_deperror = {}
667 self.addremove = false
671 -- Appends a new option
672 function AbstractSection.option(self, class, option, ...)
673 -- Autodetect from UVL
674 if class == true and self.map:get_scheme(self.sectiontype, option) then
675 local vs = self.map:get_scheme(self.sectiontype, option)
676 if vs.type == "boolean" then
678 elseif vs.type == "list" then
680 elseif vs.type == "enum" or vs.type == "reference" then
687 if instanceof(class, AbstractValue) then
688 local obj = class(self.map, self, option, ...)
690 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
693 self.fields[option] = obj
695 elseif class == true then
696 error("No valid class was given and autodetection failed.")
698 error("class must be a descendant of AbstractValue")
702 -- Parse optional options
703 function AbstractSection.parse_optionals(self, section)
704 if not self.optional then
708 self.optionals[section] = {}
710 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
711 for k,v in ipairs(self.children) do
712 if v.optional and not v:cfgvalue(section) then
713 if field == v.option then
716 table.insert(self.optionals[section], v)
721 if field and #field > 0 and self.dynamic then
722 self:add_dynamic(field)
726 -- Add a dynamic option
727 function AbstractSection.add_dynamic(self, field, optional)
728 local o = self:option(Value, field, field)
729 o.optional = optional
732 -- Parse all dynamic options
733 function AbstractSection.parse_dynamic(self, section)
734 if not self.dynamic then
738 local arr = luci.util.clone(self:cfgvalue(section))
739 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
740 for k, v in pairs(form) do
744 for key,val in pairs(arr) do
747 for i,c in ipairs(self.children) do
748 if c.option == key then
753 if create and key:sub(1, 1) ~= "." then
754 self:add_dynamic(key, true)
759 -- Returns the section's UCI table
760 function AbstractSection.cfgvalue(self, section)
761 return self.map:get(section)
765 function AbstractSection.push_events(self)
766 --luci.util.append(self.map.events, self.events)
767 self.map.changed = true
770 -- Removes the section
771 function AbstractSection.remove(self, section)
772 return self.map:del(section)
775 -- Creates the section
776 function AbstractSection.create(self, section)
780 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
782 section = self.map:add(self.sectiontype)
787 for k,v in pairs(self.children) do
789 self.map:set(section, v.option, v.default)
793 for k,v in pairs(self.defaults) do
794 self.map:set(section, k, v)
802 SimpleSection = class(AbstractSection)
804 function SimpleSection.__init__(self, form, ...)
805 AbstractSection.__init__(self, form, nil, ...)
806 self.template = "cbi/nullsection"
810 Table = class(AbstractSection)
812 function Table.__init__(self, form, data, ...)
813 local datasource = {}
814 datasource.config = "table"
817 datasource.formvalue = Map.formvalue
818 datasource.formvaluetable = Map.formvaluetable
819 datasource.readinput = true
821 function datasource.get(self, section, option)
822 return data[section] and data[section][option]
825 function datasource.submitstate(self)
826 return Map.formvalue(self, "cbi.submit")
829 function datasource.del(...)
833 function datasource.get_scheme()
837 AbstractSection.__init__(self, datasource, "table", ...)
838 self.template = "cbi/tblsection"
839 self.rowcolors = true
840 self.anonymous = true
843 function Table.parse(self, readinput)
844 self.map.readinput = (readinput ~= false)
845 for i, k in ipairs(self:cfgsections()) do
846 if self.map:submitstate() then
852 function Table.cfgsections(self)
855 for i, v in luci.util.kspairs(self.data) do
856 table.insert(sections, i)
865 NamedSection - A fixed configuration section defined by its name
867 NamedSection = class(AbstractSection)
869 function NamedSection.__init__(self, map, section, stype, ...)
870 AbstractSection.__init__(self, map, stype, ...)
871 Node._i18n(self, map.config, section, nil, ...)
874 self.addremove = false
876 -- Use defaults from UVL
877 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
878 local vs = self.map:get_scheme(self.sectiontype)
879 self.addremove = not vs.unique and not vs.required
880 self.dynamic = vs.dynamic
881 self.title = self.title or vs.title
882 self.description = self.description or vs.descr
885 self.template = "cbi/nsection"
886 self.section = section
889 function NamedSection.parse(self, novld)
890 local s = self.section
891 local active = self:cfgvalue(s)
893 if self.addremove then
894 local path = self.config.."."..s
895 if active then -- Remove the section
896 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
900 else -- Create and apply default values
901 if self.map:formvalue("cbi.cns."..path) then
909 AbstractSection.parse_dynamic(self, s)
910 if self.map:submitstate() then
913 if not novld and not self.override_scheme and self.map.scheme then
914 _uvl_validate_section(self, s)
917 AbstractSection.parse_optionals(self, s)
927 TypedSection - A (set of) configuration section(s) defined by the type
928 addremove: Defines whether the user can add/remove sections of this type
929 anonymous: Allow creating anonymous sections
930 validate: a validation function returning nil if the section is invalid
932 TypedSection = class(AbstractSection)
934 function TypedSection.__init__(self, map, type, ...)
935 AbstractSection.__init__(self, map, type, ...)
936 Node._i18n(self, map.config, type, nil, ...)
938 self.template = "cbi/tsection"
940 self.anonymous = false
942 -- Use defaults from UVL
943 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
944 local vs = self.map:get_scheme(self.sectiontype)
945 self.addremove = not vs.unique and not vs.required
946 self.dynamic = vs.dynamic
947 self.anonymous = not vs.named
948 self.title = self.title or vs.title
949 self.description = self.description or vs.descr
953 -- Return all matching UCI sections for this TypedSection
954 function TypedSection.cfgsections(self)
956 self.map.uci:foreach(self.map.config, self.sectiontype,
958 if self:checkscope(section[".name"]) then
959 table.insert(sections, section[".name"])
966 -- Limits scope to sections that have certain option => value pairs
967 function TypedSection.depends(self, option, value)
968 table.insert(self.deps, {option=option, value=value})
971 function TypedSection.parse(self, novld)
972 if self.addremove then
974 local crval = REMOVE_PREFIX .. self.config
975 local name = self.map:formvaluetable(crval)
976 for k,v in pairs(name) do
977 if k:sub(-2) == ".x" then
980 if self:cfgvalue(k) and self:checkscope(k) then
987 for i, k in ipairs(self:cfgsections()) do
988 AbstractSection.parse_dynamic(self, k)
989 if self.map:submitstate() then
990 Node.parse(self, k, novld)
992 if not novld and not self.override_scheme and self.map.scheme then
993 _uvl_validate_section(self, k)
996 AbstractSection.parse_optionals(self, k)
999 if self.addremove then
1002 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1003 local name = self.map:formvalue(crval)
1004 if self.anonymous then
1006 created = self:create()
1010 -- Ignore if it already exists
1011 if self:cfgvalue(name) then
1015 name = self:checkscope(name)
1018 self.err_invalid = true
1021 if name and #name > 0 then
1022 created = self:create(name) and name
1024 self.invalid_cts = true
1031 AbstractSection.parse_optionals(self, created)
1035 if created or self.changed then
1040 -- Verifies scope of sections
1041 function TypedSection.checkscope(self, section)
1042 -- Check if we are not excluded
1043 if self.filter and not self:filter(section) then
1047 -- Check if at least one dependency is met
1048 if #self.deps > 0 and self:cfgvalue(section) then
1051 for k, v in ipairs(self.deps) do
1052 if self:cfgvalue(section)[v.option] == v.value then
1062 return self:validate(section)
1066 -- Dummy validate function
1067 function TypedSection.validate(self, section)
1073 AbstractValue - An abstract Value Type
1074 null: Value can be empty
1075 valid: A function returning the value if it is valid otherwise nil
1076 depends: A table of option => value pairs of which one must be true
1077 default: The default value
1078 size: The size of the input fields
1079 rmempty: Unset value if empty
1080 optional: This value is optional (see AbstractSection.optionals)
1082 AbstractValue = class(Node)
1084 function AbstractValue.__init__(self, map, section, option, ...)
1085 Node.__init__(self, ...)
1086 self.section = section
1087 self.option = option
1089 self.config = map.config
1090 self.tag_invalid = {}
1091 self.tag_missing = {}
1092 self.tag_reqerror = {}
1095 --self.cast = "string"
1097 self.track_missing = false
1101 self.optional = false
1104 function AbstractValue.prepare(self)
1105 -- Use defaults from UVL
1106 if not self.override_scheme
1107 and self.map:get_scheme(self.section.sectiontype, self.option) then
1108 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1109 if self.cast == nil then
1110 self.cast = (vs.type == "list") and "list" or "string"
1112 self.title = self.title or vs.title
1113 self.description = self.description or vs.descr
1114 if self.default == nil then
1115 self.default = vs.default
1118 if vs.depends and not self.override_dependencies then
1119 for i, deps in ipairs(vs.depends) do
1120 deps = _uvl_strip_remote_dependencies(deps)
1128 self.cast = self.cast or "string"
1131 -- Add a dependencie to another section field
1132 function AbstractValue.depends(self, field, value)
1134 if type(field) == "string" then
1141 table.insert(self.deps, {deps=deps, add=""})
1144 -- Generates the unique CBID
1145 function AbstractValue.cbid(self, section)
1146 return "cbid."..self.map.config.."."..section.."."..self.option
1149 -- Return whether this object should be created
1150 function AbstractValue.formcreated(self, section)
1151 local key = "cbi.opt."..self.config.."."..section
1152 return (self.map:formvalue(key) == self.option)
1155 -- Returns the formvalue for this object
1156 function AbstractValue.formvalue(self, section)
1157 return self.map:formvalue(self:cbid(section))
1160 function AbstractValue.additional(self, value)
1161 self.optional = value
1164 function AbstractValue.mandatory(self, value)
1165 self.rmempty = not value
1168 function AbstractValue.parse(self, section, novld)
1169 local fvalue = self:formvalue(section)
1170 local cvalue = self:cfgvalue(section)
1172 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1173 fvalue = self:transform(self:validate(fvalue, section))
1174 if not fvalue and not novld then
1176 self.error[section] = "invalid"
1178 self.error = { [section] = "invalid" }
1180 self.map.save = false
1182 if fvalue and not (fvalue == cvalue) then
1183 if self:write(section, fvalue) then
1185 self.section.changed = true
1186 --luci.util.append(self.map.events, self.events)
1189 else -- Unset the UCI or error
1190 if self.rmempty or self.optional then
1191 if self:remove(section) then
1193 self.section.changed = true
1194 --luci.util.append(self.map.events, self.events)
1196 elseif cvalue ~= fvalue and not novld then
1197 self:write(section, fvalue or "")
1199 self.error[section] = "missing"
1201 self.error = { [section] = "missing" }
1203 self.map.save = false
1208 -- Render if this value exists or if it is mandatory
1209 function AbstractValue.render(self, s, scope)
1210 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1213 scope.cbid = self:cbid(s)
1214 scope.striptags = luci.util.striptags
1216 scope.ifattr = function(cond,key,val)
1218 return string.format(
1219 ' %s="%s"', tostring(key),
1220 luci.util.pcdata(tostring( val
1222 or (type(self[key]) ~= "function" and self[key])
1230 scope.attr = function(...)
1231 return scope.ifattr( true, ... )
1234 Node.render(self, scope)
1238 -- Return the UCI value of this object
1239 function AbstractValue.cfgvalue(self, section)
1240 local value = self.map:get(section, self.option)
1243 elseif not self.cast or self.cast == type(value) then
1245 elseif self.cast == "string" then
1246 if type(value) == "table" then
1249 elseif self.cast == "table" then
1250 return luci.util.split(value, "%s+", nil, true)
1254 -- Validate the form value
1255 function AbstractValue.validate(self, value)
1259 AbstractValue.transform = AbstractValue.validate
1263 function AbstractValue.write(self, section, value)
1264 return self.map:set(section, self.option, value)
1268 function AbstractValue.remove(self, section)
1269 return self.map:del(section, self.option)
1276 Value - A one-line value
1277 maxlength: The maximum length
1279 Value = class(AbstractValue)
1281 function Value.__init__(self, ...)
1282 AbstractValue.__init__(self, ...)
1283 self.template = "cbi/value"
1288 function Value.value(self, key, val)
1290 table.insert(self.keylist, tostring(key))
1291 table.insert(self.vallist, tostring(val))
1295 -- DummyValue - This does nothing except being there
1296 DummyValue = class(AbstractValue)
1298 function DummyValue.__init__(self, ...)
1299 AbstractValue.__init__(self, ...)
1300 self.template = "cbi/dvalue"
1304 function DummyValue.cfgvalue(self, section)
1307 if type(self.value) == "function" then
1308 value = self:value(section)
1313 value = AbstractValue.cfgvalue(self, section)
1318 function DummyValue.parse(self)
1324 Flag - A flag being enabled or disabled
1326 Flag = class(AbstractValue)
1328 function Flag.__init__(self, ...)
1329 AbstractValue.__init__(self, ...)
1330 self.template = "cbi/fvalue"
1336 -- A flag can only have two states: set or unset
1337 function Flag.parse(self, section)
1338 local fvalue = self:formvalue(section)
1341 fvalue = self.enabled
1343 fvalue = self.disabled
1346 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1347 if not(fvalue == self:cfgvalue(section)) then
1348 self:write(section, fvalue)
1351 self:remove(section)
1358 ListValue - A one-line value predefined in a list
1359 widget: The widget that will be used (select, radio)
1361 ListValue = class(AbstractValue)
1363 function ListValue.__init__(self, ...)
1364 AbstractValue.__init__(self, ...)
1365 self.template = "cbi/lvalue"
1370 self.widget = "select"
1373 function ListValue.prepare(self, ...)
1374 AbstractValue.prepare(self, ...)
1375 if not self.override_scheme
1376 and self.map:get_scheme(self.section.sectiontype, self.option) then
1377 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1378 if self.value and vs.valuelist and not self.override_values then
1379 for k, v in ipairs(vs.valuelist) do
1381 if not self.override_dependencies
1382 and vs.enum_depends and vs.enum_depends[v.value] then
1383 for i, dep in ipairs(vs.enum_depends[v.value]) do
1384 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1387 self:value(v.value, v.title or v.value, unpack(deps))
1393 function ListValue.value(self, key, val, ...)
1394 if luci.util.contains(self.keylist, key) then
1399 table.insert(self.keylist, tostring(key))
1400 table.insert(self.vallist, tostring(val))
1402 for i, deps in ipairs({...}) do
1403 table.insert(self.deps, {add = "-"..key, deps=deps})
1407 function ListValue.validate(self, val)
1408 if luci.util.contains(self.keylist, val) then
1418 MultiValue - Multiple delimited values
1419 widget: The widget that will be used (select, checkbox)
1420 delimiter: The delimiter that will separate the values (default: " ")
1422 MultiValue = class(AbstractValue)
1424 function MultiValue.__init__(self, ...)
1425 AbstractValue.__init__(self, ...)
1426 self.template = "cbi/mvalue"
1431 self.widget = "checkbox"
1432 self.delimiter = " "
1435 function MultiValue.render(self, ...)
1436 if self.widget == "select" and not self.size then
1437 self.size = #self.vallist
1440 AbstractValue.render(self, ...)
1443 function MultiValue.value(self, key, val)
1444 if luci.util.contains(self.keylist, key) then
1449 table.insert(self.keylist, tostring(key))
1450 table.insert(self.vallist, tostring(val))
1453 function MultiValue.valuelist(self, section)
1454 local val = self:cfgvalue(section)
1456 if not(type(val) == "string") then
1460 return luci.util.split(val, self.delimiter)
1463 function MultiValue.validate(self, val)
1464 val = (type(val) == "table") and val or {val}
1468 for i, value in ipairs(val) do
1469 if luci.util.contains(self.keylist, value) then
1470 result = result and (result .. self.delimiter .. value) or value
1478 StaticList = class(MultiValue)
1480 function StaticList.__init__(self, ...)
1481 MultiValue.__init__(self, ...)
1483 self.valuelist = self.cfgvalue
1485 if not self.override_scheme
1486 and self.map:get_scheme(self.section.sectiontype, self.option) then
1487 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1488 if self.value and vs.values and not self.override_values then
1489 for k, v in pairs(vs.values) do
1496 function StaticList.validate(self, value)
1497 value = (type(value) == "table") and value or {value}
1500 for i, v in ipairs(value) do
1501 if luci.util.contains(self.vallist, v) then
1502 table.insert(valid, v)
1509 DynamicList = class(AbstractValue)
1511 function DynamicList.__init__(self, ...)
1512 AbstractValue.__init__(self, ...)
1513 self.template = "cbi/dynlist"
1519 function DynamicList.value(self, key, val)
1521 table.insert(self.keylist, tostring(key))
1522 table.insert(self.vallist, tostring(val))
1525 function DynamicList.formvalue(self, section)
1526 local value = AbstractValue.formvalue(self, section)
1527 value = (type(value) == "table") and value or {value}
1530 for i, v in ipairs(value) do
1532 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1533 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1534 table.insert(valid, v)
1543 TextValue - A multi-line value
1546 TextValue = class(AbstractValue)
1548 function TextValue.__init__(self, ...)
1549 AbstractValue.__init__(self, ...)
1550 self.template = "cbi/tvalue"
1556 Button = class(AbstractValue)
1558 function Button.__init__(self, ...)
1559 AbstractValue.__init__(self, ...)
1560 self.template = "cbi/button"
1561 self.inputstyle = nil
1566 FileUpload = class(AbstractValue)
1568 function FileUpload.__init__(self, ...)
1569 AbstractValue.__init__(self, ...)
1570 self.template = "cbi/upload"
1571 if not self.map.upload_fields then
1572 self.map.upload_fields = { self }
1574 self.map.upload_fields[#self.map.upload_fields+1] = self
1578 function FileUpload.formcreated(self, section)
1579 return AbstractValue.formcreated(self, section) or
1580 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1581 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1584 function FileUpload.cfgvalue(self, section)
1585 local val = AbstractValue.cfgvalue(self, section)
1586 if val and luci.fs.access(val) then
1592 function FileUpload.formvalue(self, section)
1593 local val = AbstractValue.formvalue(self, section)
1595 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1596 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1606 function FileUpload.remove(self, section)
1607 local val = AbstractValue.formvalue(self, section)
1608 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1609 return AbstractValue.remove(self, section)
1613 FileBrowser = class(AbstractValue)
1615 function FileBrowser.__init__(self, ...)
1616 AbstractValue.__init__(self, ...)
1617 self.template = "cbi/browser"