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")
30 local util = require("luci.util")
35 --local event = require "luci.sys.event"
36 local uci = require("luci.model.uci")
37 local class = util.class
38 local instanceof = 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
582 local nrender, nstate = self:handle(state, self.data)
583 self.dorender = self.dorender or (nrender ~= false)
584 state = nstate or state
589 function SimpleForm.render(self, ...)
590 if self.dorender then
591 Node.render(self, ...)
595 function SimpleForm.submitstate(self)
596 return self:formvalue("cbi.submit")
599 function SimpleForm.section(self, class, ...)
600 if instanceof(class, AbstractSection) then
601 local obj = class(self, ...)
605 error("class must be a descendent of AbstractSection")
609 -- Creates a child field
610 function SimpleForm.field(self, class, ...)
612 for k, v in ipairs(self.children) do
613 if instanceof(v, SimpleSection) then
619 section = self:section(SimpleSection)
622 if instanceof(class, AbstractValue) then
623 local obj = class(self, section, ...)
624 obj.track_missing = true
628 error("class must be a descendent of AbstractValue")
632 function SimpleForm.set(self, section, option, value)
633 self.data[option] = value
637 function SimpleForm.del(self, section, option)
638 self.data[option] = nil
642 function SimpleForm.get(self, section, option)
643 return self.data[option]
647 function SimpleForm.get_scheme()
652 Form = class(SimpleForm)
654 function Form.__init__(self, ...)
655 SimpleForm.__init__(self, ...)
663 AbstractSection = class(Node)
665 function AbstractSection.__init__(self, map, sectiontype, ...)
666 Node.__init__(self, ...)
667 self.sectiontype = sectiontype
669 self.config = map.config
674 self.tag_invalid = {}
675 self.tag_deperror = {}
679 self.addremove = false
683 -- Appends a new option
684 function AbstractSection.option(self, class, option, ...)
685 -- Autodetect from UVL
686 if class == true and self.map:get_scheme(self.sectiontype, option) then
687 local vs = self.map:get_scheme(self.sectiontype, option)
688 if vs.type == "boolean" then
690 elseif vs.type == "list" then
692 elseif vs.type == "enum" or vs.type == "reference" then
699 if instanceof(class, AbstractValue) then
700 local obj = class(self.map, self, option, ...)
702 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
705 self.fields[option] = obj
707 elseif class == true then
708 error("No valid class was given and autodetection failed.")
710 error("class must be a descendant of AbstractValue")
714 -- Parse optional options
715 function AbstractSection.parse_optionals(self, section)
716 if not self.optional then
720 self.optionals[section] = {}
722 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
723 for k,v in ipairs(self.children) do
724 if v.optional and not v:cfgvalue(section) then
725 if field == v.option then
728 table.insert(self.optionals[section], v)
733 if field and #field > 0 and self.dynamic then
734 self:add_dynamic(field)
738 -- Add a dynamic option
739 function AbstractSection.add_dynamic(self, field, optional)
740 local o = self:option(Value, field, field)
741 o.optional = optional
744 -- Parse all dynamic options
745 function AbstractSection.parse_dynamic(self, section)
746 if not self.dynamic then
750 local arr = luci.util.clone(self:cfgvalue(section))
751 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
752 for k, v in pairs(form) do
756 for key,val in pairs(arr) do
759 for i,c in ipairs(self.children) do
760 if c.option == key then
765 if create and key:sub(1, 1) ~= "." then
766 self:add_dynamic(key, true)
771 -- Returns the section's UCI table
772 function AbstractSection.cfgvalue(self, section)
773 return self.map:get(section)
777 function AbstractSection.push_events(self)
778 --luci.util.append(self.map.events, self.events)
779 self.map.changed = true
782 -- Removes the section
783 function AbstractSection.remove(self, section)
784 self.map.autoapply = false
785 return self.map:del(section)
788 -- Creates the section
789 function AbstractSection.create(self, section)
793 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
795 section = self.map:add(self.sectiontype)
800 for k,v in pairs(self.children) do
802 self.map:set(section, v.option, v.default)
806 for k,v in pairs(self.defaults) do
807 self.map:set(section, k, v)
811 self.map.autoapply = false
817 SimpleSection = class(AbstractSection)
819 function SimpleSection.__init__(self, form, ...)
820 AbstractSection.__init__(self, form, nil, ...)
821 self.template = "cbi/nullsection"
825 Table = class(AbstractSection)
827 function Table.__init__(self, form, data, ...)
828 local datasource = {}
830 datasource.config = "table"
831 self.data = data or {}
833 datasource.formvalue = Map.formvalue
834 datasource.formvaluetable = Map.formvaluetable
835 datasource.readinput = true
837 function datasource.get(self, section, option)
838 return tself.data[section] and tself.data[section][option]
841 function datasource.submitstate(self)
842 return Map.formvalue(self, "cbi.submit")
845 function datasource.del(...)
849 function datasource.get_scheme()
853 AbstractSection.__init__(self, datasource, "table", ...)
854 self.template = "cbi/tblsection"
855 self.rowcolors = true
856 self.anonymous = true
859 function Table.parse(self, readinput)
860 self.map.readinput = (readinput ~= false)
861 for i, k in ipairs(self:cfgsections()) do
862 if self.map:submitstate() then
868 function Table.cfgsections(self)
871 for i, v in luci.util.kspairs(self.data) do
872 table.insert(sections, i)
878 function Table.update(self, data)
885 NamedSection - A fixed configuration section defined by its name
887 NamedSection = class(AbstractSection)
889 function NamedSection.__init__(self, map, section, stype, ...)
890 AbstractSection.__init__(self, map, stype, ...)
891 Node._i18n(self, map.config, section, nil, ...)
894 self.addremove = false
896 -- Use defaults from UVL
897 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
898 local vs = self.map:get_scheme(self.sectiontype)
899 self.addremove = not vs.unique and not vs.required
900 self.dynamic = vs.dynamic
901 self.title = self.title or vs.title
902 self.description = self.description or vs.descr
905 self.template = "cbi/nsection"
906 self.section = section
909 function NamedSection.parse(self, novld)
910 local s = self.section
911 local active = self:cfgvalue(s)
913 if self.addremove then
914 local path = self.config.."."..s
915 if active then -- Remove the section
916 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
920 else -- Create and apply default values
921 if self.map:formvalue("cbi.cns."..path) then
929 AbstractSection.parse_dynamic(self, s)
930 if self.map:submitstate() then
933 if not novld and not self.override_scheme and self.map.scheme then
934 _uvl_validate_section(self, s)
937 AbstractSection.parse_optionals(self, s)
947 TypedSection - A (set of) configuration section(s) defined by the type
948 addremove: Defines whether the user can add/remove sections of this type
949 anonymous: Allow creating anonymous sections
950 validate: a validation function returning nil if the section is invalid
952 TypedSection = class(AbstractSection)
954 function TypedSection.__init__(self, map, type, ...)
955 AbstractSection.__init__(self, map, type, ...)
956 Node._i18n(self, map.config, type, nil, ...)
958 self.template = "cbi/tsection"
960 self.anonymous = false
962 -- Use defaults from UVL
963 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
964 local vs = self.map:get_scheme(self.sectiontype)
965 self.addremove = not vs.unique and not vs.required
966 self.dynamic = vs.dynamic
967 self.anonymous = not vs.named
968 self.title = self.title or vs.title
969 self.description = self.description or vs.descr
973 -- Return all matching UCI sections for this TypedSection
974 function TypedSection.cfgsections(self)
976 self.map.uci:foreach(self.map.config, self.sectiontype,
978 if self:checkscope(section[".name"]) then
979 table.insert(sections, section[".name"])
986 -- Limits scope to sections that have certain option => value pairs
987 function TypedSection.depends(self, option, value)
988 table.insert(self.deps, {option=option, value=value})
991 function TypedSection.parse(self, novld)
992 if self.addremove then
994 local crval = REMOVE_PREFIX .. self.config
995 local name = self.map:formvaluetable(crval)
996 for k,v in pairs(name) do
997 if k:sub(-2) == ".x" then
1000 if self:cfgvalue(k) and self:checkscope(k) then
1007 for i, k in ipairs(self:cfgsections()) do
1008 AbstractSection.parse_dynamic(self, k)
1009 if self.map:submitstate() then
1010 Node.parse(self, k, novld)
1012 if not novld and not self.override_scheme and self.map.scheme then
1013 _uvl_validate_section(self, k)
1016 AbstractSection.parse_optionals(self, k)
1019 if self.addremove then
1022 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1023 local name = self.map:formvalue(crval)
1024 if self.anonymous then
1026 created = self:create()
1030 -- Ignore if it already exists
1031 if self:cfgvalue(name) then
1035 name = self:checkscope(name)
1038 self.err_invalid = true
1041 if name and #name > 0 then
1042 created = self:create(name) and name
1044 self.invalid_cts = true
1051 AbstractSection.parse_optionals(self, created)
1055 if created or self.changed then
1060 -- Verifies scope of sections
1061 function TypedSection.checkscope(self, section)
1062 -- Check if we are not excluded
1063 if self.filter and not self:filter(section) then
1067 -- Check if at least one dependency is met
1068 if #self.deps > 0 and self:cfgvalue(section) then
1071 for k, v in ipairs(self.deps) do
1072 if self:cfgvalue(section)[v.option] == v.value then
1082 return self:validate(section)
1086 -- Dummy validate function
1087 function TypedSection.validate(self, section)
1093 AbstractValue - An abstract Value Type
1094 null: Value can be empty
1095 valid: A function returning the value if it is valid otherwise nil
1096 depends: A table of option => value pairs of which one must be true
1097 default: The default value
1098 size: The size of the input fields
1099 rmempty: Unset value if empty
1100 optional: This value is optional (see AbstractSection.optionals)
1102 AbstractValue = class(Node)
1104 function AbstractValue.__init__(self, map, section, option, ...)
1105 Node.__init__(self, ...)
1106 self.section = section
1107 self.option = option
1109 self.config = map.config
1110 self.tag_invalid = {}
1111 self.tag_missing = {}
1112 self.tag_reqerror = {}
1115 --self.cast = "string"
1117 self.track_missing = false
1121 self.optional = false
1124 function AbstractValue.prepare(self)
1125 -- Use defaults from UVL
1126 if not self.override_scheme
1127 and self.map:get_scheme(self.section.sectiontype, self.option) then
1128 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1129 if self.cast == nil then
1130 self.cast = (vs.type == "list") and "list" or "string"
1132 self.title = self.title or vs.title
1133 self.description = self.description or vs.descr
1134 if self.default == nil then
1135 self.default = vs.default
1138 if vs.depends and not self.override_dependencies then
1139 for i, deps in ipairs(vs.depends) do
1140 deps = _uvl_strip_remote_dependencies(deps)
1148 self.cast = self.cast or "string"
1151 -- Add a dependencie to another section field
1152 function AbstractValue.depends(self, field, value)
1154 if type(field) == "string" then
1161 table.insert(self.deps, {deps=deps, add=""})
1164 -- Generates the unique CBID
1165 function AbstractValue.cbid(self, section)
1166 return "cbid."..self.map.config.."."..section.."."..self.option
1169 -- Return whether this object should be created
1170 function AbstractValue.formcreated(self, section)
1171 local key = "cbi.opt."..self.config.."."..section
1172 return (self.map:formvalue(key) == self.option)
1175 -- Returns the formvalue for this object
1176 function AbstractValue.formvalue(self, section)
1177 return self.map:formvalue(self:cbid(section))
1180 function AbstractValue.additional(self, value)
1181 self.optional = value
1184 function AbstractValue.mandatory(self, value)
1185 self.rmempty = not value
1188 function AbstractValue.parse(self, section, novld)
1189 local fvalue = self:formvalue(section)
1190 local cvalue = self:cfgvalue(section)
1192 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1193 fvalue = self:transform(self:validate(fvalue, section))
1194 if not fvalue and not novld then
1196 self.error[section] = "invalid"
1198 self.error = { [section] = "invalid" }
1200 self.map.save = false
1202 if fvalue and not (fvalue == cvalue) then
1203 if self:write(section, fvalue) then
1205 self.section.changed = true
1206 --luci.util.append(self.map.events, self.events)
1209 else -- Unset the UCI or error
1210 if self.rmempty or self.optional then
1211 if self:remove(section) then
1213 self.section.changed = true
1214 --luci.util.append(self.map.events, self.events)
1216 elseif cvalue ~= fvalue and not novld then
1217 self:write(section, fvalue or "")
1219 self.error[section] = "missing"
1221 self.error = { [section] = "missing" }
1223 self.map.save = false
1228 -- Render if this value exists or if it is mandatory
1229 function AbstractValue.render(self, s, scope)
1230 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1233 scope.cbid = self:cbid(s)
1234 scope.striptags = luci.util.striptags
1236 scope.ifattr = function(cond,key,val)
1238 return string.format(
1239 ' %s="%s"', tostring(key),
1240 luci.util.pcdata(tostring( val
1242 or (type(self[key]) ~= "function" and self[key])
1250 scope.attr = function(...)
1251 return scope.ifattr( true, ... )
1254 Node.render(self, scope)
1258 -- Return the UCI value of this object
1259 function AbstractValue.cfgvalue(self, section)
1260 local value = self.map:get(section, self.option)
1263 elseif not self.cast or self.cast == type(value) then
1265 elseif self.cast == "string" then
1266 if type(value) == "table" then
1269 elseif self.cast == "table" then
1270 return luci.util.split(value, "%s+", nil, true)
1274 -- Validate the form value
1275 function AbstractValue.validate(self, value)
1279 AbstractValue.transform = AbstractValue.validate
1283 function AbstractValue.write(self, section, value)
1284 return self.map:set(section, self.option, value)
1288 function AbstractValue.remove(self, section)
1289 return self.map:del(section, self.option)
1296 Value - A one-line value
1297 maxlength: The maximum length
1299 Value = class(AbstractValue)
1301 function Value.__init__(self, ...)
1302 AbstractValue.__init__(self, ...)
1303 self.template = "cbi/value"
1308 function Value.value(self, key, val)
1310 table.insert(self.keylist, tostring(key))
1311 table.insert(self.vallist, tostring(val))
1315 -- DummyValue - This does nothing except being there
1316 DummyValue = class(AbstractValue)
1318 function DummyValue.__init__(self, ...)
1319 AbstractValue.__init__(self, ...)
1320 self.template = "cbi/dvalue"
1324 function DummyValue.cfgvalue(self, section)
1327 if type(self.value) == "function" then
1328 value = self:value(section)
1333 value = AbstractValue.cfgvalue(self, section)
1338 function DummyValue.parse(self)
1344 Flag - A flag being enabled or disabled
1346 Flag = class(AbstractValue)
1348 function Flag.__init__(self, ...)
1349 AbstractValue.__init__(self, ...)
1350 self.template = "cbi/fvalue"
1356 -- A flag can only have two states: set or unset
1357 function Flag.parse(self, section)
1358 local fvalue = self:formvalue(section)
1361 fvalue = self.enabled
1363 fvalue = self.disabled
1366 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1367 if not(fvalue == self:cfgvalue(section)) then
1368 self:write(section, fvalue)
1371 self:remove(section)
1378 ListValue - A one-line value predefined in a list
1379 widget: The widget that will be used (select, radio)
1381 ListValue = class(AbstractValue)
1383 function ListValue.__init__(self, ...)
1384 AbstractValue.__init__(self, ...)
1385 self.template = "cbi/lvalue"
1390 self.widget = "select"
1393 function ListValue.prepare(self, ...)
1394 AbstractValue.prepare(self, ...)
1395 if not self.override_scheme
1396 and self.map:get_scheme(self.section.sectiontype, self.option) then
1397 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1398 if self.value and vs.valuelist and not self.override_values then
1399 for k, v in ipairs(vs.valuelist) do
1401 if not self.override_dependencies
1402 and vs.enum_depends and vs.enum_depends[v.value] then
1403 for i, dep in ipairs(vs.enum_depends[v.value]) do
1404 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1407 self:value(v.value, v.title or v.value, unpack(deps))
1413 function ListValue.value(self, key, val, ...)
1414 if luci.util.contains(self.keylist, key) then
1419 table.insert(self.keylist, tostring(key))
1420 table.insert(self.vallist, tostring(val))
1422 for i, deps in ipairs({...}) do
1423 table.insert(self.deps, {add = "-"..key, deps=deps})
1427 function ListValue.validate(self, val)
1428 if luci.util.contains(self.keylist, val) then
1438 MultiValue - Multiple delimited values
1439 widget: The widget that will be used (select, checkbox)
1440 delimiter: The delimiter that will separate the values (default: " ")
1442 MultiValue = class(AbstractValue)
1444 function MultiValue.__init__(self, ...)
1445 AbstractValue.__init__(self, ...)
1446 self.template = "cbi/mvalue"
1451 self.widget = "checkbox"
1452 self.delimiter = " "
1455 function MultiValue.render(self, ...)
1456 if self.widget == "select" and not self.size then
1457 self.size = #self.vallist
1460 AbstractValue.render(self, ...)
1463 function MultiValue.value(self, key, val)
1464 if luci.util.contains(self.keylist, key) then
1469 table.insert(self.keylist, tostring(key))
1470 table.insert(self.vallist, tostring(val))
1473 function MultiValue.valuelist(self, section)
1474 local val = self:cfgvalue(section)
1476 if not(type(val) == "string") then
1480 return luci.util.split(val, self.delimiter)
1483 function MultiValue.validate(self, val)
1484 val = (type(val) == "table") and val or {val}
1488 for i, value in ipairs(val) do
1489 if luci.util.contains(self.keylist, value) then
1490 result = result and (result .. self.delimiter .. value) or value
1498 StaticList = class(MultiValue)
1500 function StaticList.__init__(self, ...)
1501 MultiValue.__init__(self, ...)
1503 self.valuelist = self.cfgvalue
1505 if not self.override_scheme
1506 and self.map:get_scheme(self.section.sectiontype, self.option) then
1507 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1508 if self.value and vs.values and not self.override_values then
1509 for k, v in pairs(vs.values) do
1516 function StaticList.validate(self, value)
1517 value = (type(value) == "table") and value or {value}
1520 for i, v in ipairs(value) do
1521 if luci.util.contains(self.keylist, v) then
1522 table.insert(valid, v)
1529 DynamicList = class(AbstractValue)
1531 function DynamicList.__init__(self, ...)
1532 AbstractValue.__init__(self, ...)
1533 self.template = "cbi/dynlist"
1539 function DynamicList.value(self, key, val)
1541 table.insert(self.keylist, tostring(key))
1542 table.insert(self.vallist, tostring(val))
1545 function DynamicList.formvalue(self, section)
1546 local value = AbstractValue.formvalue(self, section)
1547 value = (type(value) == "table") and value or {value}
1550 for i, v in ipairs(value) do
1552 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1553 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1554 table.insert(valid, v)
1563 TextValue - A multi-line value
1566 TextValue = class(AbstractValue)
1568 function TextValue.__init__(self, ...)
1569 AbstractValue.__init__(self, ...)
1570 self.template = "cbi/tvalue"
1576 Button = class(AbstractValue)
1578 function Button.__init__(self, ...)
1579 AbstractValue.__init__(self, ...)
1580 self.template = "cbi/button"
1581 self.inputstyle = nil
1586 FileUpload = class(AbstractValue)
1588 function FileUpload.__init__(self, ...)
1589 AbstractValue.__init__(self, ...)
1590 self.template = "cbi/upload"
1591 if not self.map.upload_fields then
1592 self.map.upload_fields = { self }
1594 self.map.upload_fields[#self.map.upload_fields+1] = self
1598 function FileUpload.formcreated(self, section)
1599 return AbstractValue.formcreated(self, section) or
1600 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1601 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1604 function FileUpload.cfgvalue(self, section)
1605 local val = AbstractValue.cfgvalue(self, section)
1606 if val and luci.fs.access(val) then
1612 function FileUpload.formvalue(self, section)
1613 local val = AbstractValue.formvalue(self, section)
1615 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1616 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1626 function FileUpload.remove(self, section)
1627 local val = AbstractValue.formvalue(self, section)
1628 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1629 return AbstractValue.remove(self, section)
1633 FileBrowser = class(AbstractValue)
1635 function FileBrowser.__init__(self, ...)
1636 AbstractValue.__init__(self, ...)
1637 self.template = "cbi/browser"