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 or { e }) 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
300 self.uci = uci.cursor()
305 if not self.uci:load(self.config) then
306 error("Unable to read UCI data: " .. self.config)
309 self.validator = luci.uvl.UVL()
310 self.scheme = self.validator:get_scheme(self.config)
314 function Map.formvalue(self, key)
315 return self.readinput and luci.http.formvalue(key)
318 function Map.formvaluetable(self, key)
319 return self.readinput and luci.http.formvaluetable(key)
322 function Map.get_scheme(self, sectiontype, option)
324 return self.scheme and self.scheme.sections[sectiontype]
326 return self.scheme and self.scheme.variables[sectiontype]
327 and self.scheme.variables[sectiontype][option]
331 function Map.submitstate(self)
332 return self:formvalue("cbi.submit")
335 -- Chain foreign config
336 function Map.chain(self, config)
337 table.insert(self.parsechain, config)
340 function Map.state_handler(self, state)
344 -- Use optimized UCI writing
345 function Map.parse(self, readinput, ...)
346 self.readinput = (readinput ~= false)
347 Node.parse(self, ...)
350 for i, config in ipairs(self.parsechain) do
351 self.uci:save(config)
353 if self:submitstate() and not self.proceed and (self.autoapply or luci.http.formvalue("cbi.apply")) then
354 for i, config in ipairs(self.parsechain) do
355 self.uci:commit(config)
357 -- Refresh data because commit changes section names
358 self.uci:load(config)
360 if self.apply_on_parse then
361 self.uci:apply(self.parsechain)
363 self._apply = function()
364 local cmd = self.uci:apply(self.parsechain, true)
370 Node.parse(self, true)
373 for i, config in ipairs(self.parsechain) do
374 self.uci:unload(config)
376 if type(self.commit_handler) == "function" then
377 self:commit_handler(self:submitstate())
381 if self:submitstate() then
382 if not self.save then
383 self.state = FORM_INVALID
384 elseif self.proceed then
385 self.state = FORM_PROCEED
387 self.state = self.changed and FORM_CHANGED or FORM_VALID
390 self.state = FORM_NODATA
393 return self:state_handler(self.state)
396 function Map.render(self, ...)
397 Node.render(self, ...)
399 local fp = self._apply()
405 -- Creates a child section
406 function Map.section(self, class, ...)
407 if instanceof(class, AbstractSection) then
408 local obj = class(self, ...)
412 error("class must be a descendent of AbstractSection")
417 function Map.add(self, sectiontype)
418 return self.uci:add(self.config, sectiontype)
422 function Map.set(self, section, option, value)
424 return self.uci:set(self.config, section, option, value)
426 return self.uci:set(self.config, section, value)
431 function Map.del(self, section, option)
433 return self.uci:delete(self.config, section, option)
435 return self.uci:delete(self.config, section)
440 function Map.get(self, section, option)
442 return self.uci:get_all(self.config)
444 return self.uci:get(self.config, section, option)
446 return self.uci:get_all(self.config, section)
453 Compound = class(Node)
455 function Compound.__init__(self, ...)
457 self.children = {...}
460 function Compound.parse(self, ...)
461 local cstate, state = 0, 0
463 for k, child in ipairs(self.children) do
464 cstate = child:parse(...)
465 state = (not state or cstate < state) and cstate or state
473 Delegator - Node controller
475 Delegator = class(Node)
476 function Delegator.__init__(self, ...)
477 Node.__init__(self, ...)
479 self.template = "cbi/delegator"
482 function Delegator.state(self, name, node, transitor)
483 transitor = transitor or self.transistor_linear
484 local state = {node=node, name=name, transitor=transitor}
486 assert(instanceof(node, Node), "Invalid node")
487 assert(not self.nodes[name], "Duplicate entry")
489 self.nodes[name] = state
495 function Delegator.get(self, name)
496 return self.nodes[name]
499 function Delegator.transistor_linear(self, state, cstate)
501 for i, child in ipairs(self.children) do
502 if state == child then
503 return self.children[i+1]
511 function Delegator.parse(self, ...)
512 local active = self:getactive()
513 assert(active, "Invalid state")
515 local cstate = active.node:parse()
516 self.active = active.transistor(self, active.node, cstate)
518 if not self.active then
521 self.active:parse(false)
526 function Delegator.render(self, ...)
527 self.active.node:render(...)
530 function Delegator.getactive(self)
531 return self:get(Map.formvalue(self, "cbi.delegated")
532 or (self.children[1] and self.children[1].name))
540 Page.__init__ = Node.__init__
541 Page.parse = function() end
545 SimpleForm - A Simple non-UCI form
547 SimpleForm = class(Node)
549 function SimpleForm.__init__(self, config, title, description, data)
550 Node.__init__(self, title, description)
552 self.data = data or {}
553 self.template = "cbi/simpleform"
555 self.pageaction = false
556 self.readinput = true
559 SimpleForm.formvalue = Map.formvalue
560 SimpleForm.formvaluetable = Map.formvaluetable
562 function SimpleForm.parse(self, readinput, ...)
563 self.readinput = (readinput ~= false)
564 if self:submitstate() then
565 Node.parse(self, 1, ...)
569 for k, j in ipairs(self.children) do
570 for i, v in ipairs(j.children) do
572 and (not v.tag_missing or not v.tag_missing[1])
573 and (not v.tag_invalid or not v.tag_invalid[1])
579 not self:submitstate() and FORM_NODATA
580 or valid and FORM_VALID
583 self.dorender = not self.handle
585 local nrender, nstate = self:handle(state, self.data)
586 self.dorender = self.dorender or (nrender ~= false)
587 state = nstate or state
592 function SimpleForm.render(self, ...)
593 if self.dorender then
594 Node.render(self, ...)
598 function SimpleForm.submitstate(self)
599 return self:formvalue("cbi.submit")
602 function SimpleForm.section(self, class, ...)
603 if instanceof(class, AbstractSection) then
604 local obj = class(self, ...)
608 error("class must be a descendent of AbstractSection")
612 -- Creates a child field
613 function SimpleForm.field(self, class, ...)
615 for k, v in ipairs(self.children) do
616 if instanceof(v, SimpleSection) then
622 section = self:section(SimpleSection)
625 if instanceof(class, AbstractValue) then
626 local obj = class(self, section, ...)
627 obj.track_missing = true
631 error("class must be a descendent of AbstractValue")
635 function SimpleForm.set(self, section, option, value)
636 self.data[option] = value
640 function SimpleForm.del(self, section, option)
641 self.data[option] = nil
645 function SimpleForm.get(self, section, option)
646 return self.data[option]
650 function SimpleForm.get_scheme()
655 Form = class(SimpleForm)
657 function Form.__init__(self, ...)
658 SimpleForm.__init__(self, ...)
666 AbstractSection = class(Node)
668 function AbstractSection.__init__(self, map, sectiontype, ...)
669 Node.__init__(self, ...)
670 self.sectiontype = sectiontype
672 self.config = map.config
677 self.tag_invalid = {}
678 self.tag_deperror = {}
682 self.addremove = false
686 -- Appends a new option
687 function AbstractSection.option(self, class, option, ...)
688 -- Autodetect from UVL
689 if class == true and self.map:get_scheme(self.sectiontype, option) then
690 local vs = self.map:get_scheme(self.sectiontype, option)
691 if vs.type == "boolean" then
693 elseif vs.type == "list" then
695 elseif vs.type == "enum" or vs.type == "reference" then
702 if instanceof(class, AbstractValue) then
703 local obj = class(self.map, self, option, ...)
705 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
708 self.fields[option] = obj
710 elseif class == true then
711 error("No valid class was given and autodetection failed.")
713 error("class must be a descendant of AbstractValue")
717 -- Parse optional options
718 function AbstractSection.parse_optionals(self, section)
719 if not self.optional then
723 self.optionals[section] = {}
725 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
726 for k,v in ipairs(self.children) do
727 if v.optional and not v:cfgvalue(section) then
728 if field == v.option then
731 self.map.proceed = true
732 table.insert(self.optionals[section], v)
737 if field and #field > 0 and self.dynamic then
738 self:add_dynamic(field)
742 -- Add a dynamic option
743 function AbstractSection.add_dynamic(self, field, optional)
744 local o = self:option(Value, field, field)
745 o.optional = optional
748 -- Parse all dynamic options
749 function AbstractSection.parse_dynamic(self, section)
750 if not self.dynamic then
754 local arr = luci.util.clone(self:cfgvalue(section))
755 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
756 for k, v in pairs(form) do
760 for key,val in pairs(arr) do
763 for i,c in ipairs(self.children) do
764 if c.option == key then
769 if create and key:sub(1, 1) ~= "." then
770 self.map.proceed = true
771 self:add_dynamic(key, true)
776 -- Returns the section's UCI table
777 function AbstractSection.cfgvalue(self, section)
778 return self.map:get(section)
782 function AbstractSection.push_events(self)
783 --luci.util.append(self.map.events, self.events)
784 self.map.changed = true
787 -- Removes the section
788 function AbstractSection.remove(self, section)
789 self.map.proceed = true
790 return self.map:del(section)
793 -- Creates the section
794 function AbstractSection.create(self, section)
798 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
800 section = self.map:add(self.sectiontype)
805 for k,v in pairs(self.children) do
807 self.map:set(section, v.option, v.default)
811 for k,v in pairs(self.defaults) do
812 self.map:set(section, k, v)
816 self.map.proceed = true
822 SimpleSection = class(AbstractSection)
824 function SimpleSection.__init__(self, form, ...)
825 AbstractSection.__init__(self, form, nil, ...)
826 self.template = "cbi/nullsection"
830 Table = class(AbstractSection)
832 function Table.__init__(self, form, data, ...)
833 local datasource = {}
835 datasource.config = "table"
836 self.data = data or {}
838 datasource.formvalue = Map.formvalue
839 datasource.formvaluetable = Map.formvaluetable
840 datasource.readinput = true
842 function datasource.get(self, section, option)
843 return tself.data[section] and tself.data[section][option]
846 function datasource.submitstate(self)
847 return Map.formvalue(self, "cbi.submit")
850 function datasource.del(...)
854 function datasource.get_scheme()
858 AbstractSection.__init__(self, datasource, "table", ...)
859 self.template = "cbi/tblsection"
860 self.rowcolors = true
861 self.anonymous = true
864 function Table.parse(self, readinput)
865 self.map.readinput = (readinput ~= false)
866 for i, k in ipairs(self:cfgsections()) do
867 if self.map:submitstate() then
873 function Table.cfgsections(self)
876 for i, v in luci.util.kspairs(self.data) do
877 table.insert(sections, i)
883 function Table.update(self, data)
890 NamedSection - A fixed configuration section defined by its name
892 NamedSection = class(AbstractSection)
894 function NamedSection.__init__(self, map, section, stype, ...)
895 AbstractSection.__init__(self, map, stype, ...)
896 Node._i18n(self, map.config, section, nil, ...)
899 self.addremove = false
901 -- Use defaults from UVL
902 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
903 local vs = self.map:get_scheme(self.sectiontype)
904 self.addremove = not vs.unique and not vs.required
905 self.dynamic = vs.dynamic
906 self.title = self.title or vs.title
907 self.description = self.description or vs.descr
910 self.template = "cbi/nsection"
911 self.section = section
914 function NamedSection.parse(self, novld)
915 local s = self.section
916 local active = self:cfgvalue(s)
918 if self.addremove then
919 local path = self.config.."."..s
920 if active then -- Remove the section
921 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
925 else -- Create and apply default values
926 if self.map:formvalue("cbi.cns."..path) then
934 AbstractSection.parse_dynamic(self, s)
935 if self.map:submitstate() then
938 if not novld and not self.override_scheme and self.map.scheme then
939 _uvl_validate_section(self, s)
942 AbstractSection.parse_optionals(self, s)
952 TypedSection - A (set of) configuration section(s) defined by the type
953 addremove: Defines whether the user can add/remove sections of this type
954 anonymous: Allow creating anonymous sections
955 validate: a validation function returning nil if the section is invalid
957 TypedSection = class(AbstractSection)
959 function TypedSection.__init__(self, map, type, ...)
960 AbstractSection.__init__(self, map, type, ...)
961 Node._i18n(self, map.config, type, nil, ...)
963 self.template = "cbi/tsection"
965 self.anonymous = false
967 -- Use defaults from UVL
968 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
969 local vs = self.map:get_scheme(self.sectiontype)
970 self.addremove = not vs.unique and not vs.required
971 self.dynamic = vs.dynamic
972 self.anonymous = not vs.named
973 self.title = self.title or vs.title
974 self.description = self.description or vs.descr
978 -- Return all matching UCI sections for this TypedSection
979 function TypedSection.cfgsections(self)
981 self.map.uci:foreach(self.map.config, self.sectiontype,
983 if self:checkscope(section[".name"]) then
984 table.insert(sections, section[".name"])
991 -- Limits scope to sections that have certain option => value pairs
992 function TypedSection.depends(self, option, value)
993 table.insert(self.deps, {option=option, value=value})
996 function TypedSection.parse(self, novld)
997 if self.addremove then
999 local crval = REMOVE_PREFIX .. self.config
1000 local name = self.map:formvaluetable(crval)
1001 for k,v in pairs(name) do
1002 if k:sub(-2) == ".x" then
1003 k = k:sub(1, #k - 2)
1005 if self:cfgvalue(k) and self:checkscope(k) then
1012 for i, k in ipairs(self:cfgsections()) do
1013 AbstractSection.parse_dynamic(self, k)
1014 if self.map:submitstate() then
1015 Node.parse(self, k, novld)
1017 if not novld and not self.override_scheme and self.map.scheme then
1018 _uvl_validate_section(self, k)
1021 AbstractSection.parse_optionals(self, k)
1024 if self.addremove then
1027 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1028 local name = self.map:formvalue(crval)
1029 if self.anonymous then
1031 created = self:create()
1035 -- Ignore if it already exists
1036 if self:cfgvalue(name) then
1040 name = self:checkscope(name)
1043 self.err_invalid = true
1046 if name and #name > 0 then
1047 created = self:create(name) and name
1049 self.invalid_cts = true
1056 AbstractSection.parse_optionals(self, created)
1060 if created or self.changed then
1065 -- Verifies scope of sections
1066 function TypedSection.checkscope(self, section)
1067 -- Check if we are not excluded
1068 if self.filter and not self:filter(section) then
1072 -- Check if at least one dependency is met
1073 if #self.deps > 0 and self:cfgvalue(section) then
1076 for k, v in ipairs(self.deps) do
1077 if self:cfgvalue(section)[v.option] == v.value then
1087 return self:validate(section)
1091 -- Dummy validate function
1092 function TypedSection.validate(self, section)
1098 AbstractValue - An abstract Value Type
1099 null: Value can be empty
1100 valid: A function returning the value if it is valid otherwise nil
1101 depends: A table of option => value pairs of which one must be true
1102 default: The default value
1103 size: The size of the input fields
1104 rmempty: Unset value if empty
1105 optional: This value is optional (see AbstractSection.optionals)
1107 AbstractValue = class(Node)
1109 function AbstractValue.__init__(self, map, section, option, ...)
1110 Node.__init__(self, ...)
1111 self.section = section
1112 self.option = option
1114 self.config = map.config
1115 self.tag_invalid = {}
1116 self.tag_missing = {}
1117 self.tag_reqerror = {}
1120 --self.cast = "string"
1122 self.track_missing = false
1126 self.optional = false
1129 function AbstractValue.prepare(self)
1130 -- Use defaults from UVL
1131 if not self.override_scheme
1132 and self.map:get_scheme(self.section.sectiontype, self.option) then
1133 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1134 if self.cast == nil then
1135 self.cast = (vs.type == "list") and "list" or "string"
1137 self.title = self.title or vs.title
1138 self.description = self.description or vs.descr
1139 if self.default == nil then
1140 self.default = vs.default
1143 if vs.depends and not self.override_dependencies then
1144 for i, deps in ipairs(vs.depends) do
1145 deps = _uvl_strip_remote_dependencies(deps)
1153 self.cast = self.cast or "string"
1156 -- Add a dependencie to another section field
1157 function AbstractValue.depends(self, field, value)
1159 if type(field) == "string" then
1166 table.insert(self.deps, {deps=deps, add=""})
1169 -- Generates the unique CBID
1170 function AbstractValue.cbid(self, section)
1171 return "cbid."..self.map.config.."."..section.."."..self.option
1174 -- Return whether this object should be created
1175 function AbstractValue.formcreated(self, section)
1176 local key = "cbi.opt."..self.config.."."..section
1177 return (self.map:formvalue(key) == self.option)
1180 -- Returns the formvalue for this object
1181 function AbstractValue.formvalue(self, section)
1182 return self.map:formvalue(self:cbid(section))
1185 function AbstractValue.additional(self, value)
1186 self.optional = value
1189 function AbstractValue.mandatory(self, value)
1190 self.rmempty = not value
1193 function AbstractValue.parse(self, section, novld)
1194 local fvalue = self:formvalue(section)
1195 local cvalue = self:cfgvalue(section)
1197 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1198 fvalue = self:transform(self:validate(fvalue, section))
1199 if not fvalue and not novld then
1201 self.error[section] = "invalid"
1203 self.error = { [section] = "invalid" }
1205 self.map.save = false
1207 if fvalue and not (fvalue == cvalue) then
1208 if self:write(section, fvalue) then
1210 self.section.changed = true
1211 --luci.util.append(self.map.events, self.events)
1214 else -- Unset the UCI or error
1215 if self.rmempty or self.optional then
1216 if self:remove(section) then
1218 self.section.changed = true
1219 --luci.util.append(self.map.events, self.events)
1221 elseif cvalue ~= fvalue and not novld then
1222 self:write(section, fvalue or "")
1224 self.error[section] = "missing"
1226 self.error = { [section] = "missing" }
1228 self.map.save = false
1233 -- Render if this value exists or if it is mandatory
1234 function AbstractValue.render(self, s, scope)
1235 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1238 scope.cbid = self:cbid(s)
1239 scope.striptags = luci.util.striptags
1241 scope.ifattr = function(cond,key,val)
1243 return string.format(
1244 ' %s="%s"', tostring(key),
1245 luci.util.pcdata(tostring( val
1247 or (type(self[key]) ~= "function" and self[key])
1255 scope.attr = function(...)
1256 return scope.ifattr( true, ... )
1259 Node.render(self, scope)
1263 -- Return the UCI value of this object
1264 function AbstractValue.cfgvalue(self, section)
1265 local value = self.map:get(section, self.option)
1268 elseif not self.cast or self.cast == type(value) then
1270 elseif self.cast == "string" then
1271 if type(value) == "table" then
1274 elseif self.cast == "table" then
1275 return luci.util.split(value, "%s+", nil, true)
1279 -- Validate the form value
1280 function AbstractValue.validate(self, value)
1284 AbstractValue.transform = AbstractValue.validate
1288 function AbstractValue.write(self, section, value)
1289 return self.map:set(section, self.option, value)
1293 function AbstractValue.remove(self, section)
1294 return self.map:del(section, self.option)
1301 Value - A one-line value
1302 maxlength: The maximum length
1304 Value = class(AbstractValue)
1306 function Value.__init__(self, ...)
1307 AbstractValue.__init__(self, ...)
1308 self.template = "cbi/value"
1313 function Value.value(self, key, val)
1315 table.insert(self.keylist, tostring(key))
1316 table.insert(self.vallist, tostring(val))
1320 -- DummyValue - This does nothing except being there
1321 DummyValue = class(AbstractValue)
1323 function DummyValue.__init__(self, ...)
1324 AbstractValue.__init__(self, ...)
1325 self.template = "cbi/dvalue"
1329 function DummyValue.cfgvalue(self, section)
1332 if type(self.value) == "function" then
1333 value = self:value(section)
1338 value = AbstractValue.cfgvalue(self, section)
1343 function DummyValue.parse(self)
1349 Flag - A flag being enabled or disabled
1351 Flag = class(AbstractValue)
1353 function Flag.__init__(self, ...)
1354 AbstractValue.__init__(self, ...)
1355 self.template = "cbi/fvalue"
1361 -- A flag can only have two states: set or unset
1362 function Flag.parse(self, section)
1363 local fvalue = self:formvalue(section)
1366 fvalue = self.enabled
1368 fvalue = self.disabled
1371 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1372 if not(fvalue == self:cfgvalue(section)) then
1373 self:write(section, fvalue)
1376 self:remove(section)
1383 ListValue - A one-line value predefined in a list
1384 widget: The widget that will be used (select, radio)
1386 ListValue = class(AbstractValue)
1388 function ListValue.__init__(self, ...)
1389 AbstractValue.__init__(self, ...)
1390 self.template = "cbi/lvalue"
1395 self.widget = "select"
1398 function ListValue.prepare(self, ...)
1399 AbstractValue.prepare(self, ...)
1400 if not self.override_scheme
1401 and self.map:get_scheme(self.section.sectiontype, self.option) then
1402 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1403 if self.value and vs.valuelist and not self.override_values then
1404 for k, v in ipairs(vs.valuelist) do
1406 if not self.override_dependencies
1407 and vs.enum_depends and vs.enum_depends[v.value] then
1408 for i, dep in ipairs(vs.enum_depends[v.value]) do
1409 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1412 self:value(v.value, v.title or v.value, unpack(deps))
1418 function ListValue.value(self, key, val, ...)
1419 if luci.util.contains(self.keylist, key) then
1424 table.insert(self.keylist, tostring(key))
1425 table.insert(self.vallist, tostring(val))
1427 for i, deps in ipairs({...}) do
1428 table.insert(self.deps, {add = "-"..key, deps=deps})
1432 function ListValue.validate(self, val)
1433 if luci.util.contains(self.keylist, val) then
1443 MultiValue - Multiple delimited values
1444 widget: The widget that will be used (select, checkbox)
1445 delimiter: The delimiter that will separate the values (default: " ")
1447 MultiValue = class(AbstractValue)
1449 function MultiValue.__init__(self, ...)
1450 AbstractValue.__init__(self, ...)
1451 self.template = "cbi/mvalue"
1456 self.widget = "checkbox"
1457 self.delimiter = " "
1460 function MultiValue.render(self, ...)
1461 if self.widget == "select" and not self.size then
1462 self.size = #self.vallist
1465 AbstractValue.render(self, ...)
1468 function MultiValue.value(self, key, val)
1469 if luci.util.contains(self.keylist, key) then
1474 table.insert(self.keylist, tostring(key))
1475 table.insert(self.vallist, tostring(val))
1478 function MultiValue.valuelist(self, section)
1479 local val = self:cfgvalue(section)
1481 if not(type(val) == "string") then
1485 return luci.util.split(val, self.delimiter)
1488 function MultiValue.validate(self, val)
1489 val = (type(val) == "table") and val or {val}
1493 for i, value in ipairs(val) do
1494 if luci.util.contains(self.keylist, value) then
1495 result = result and (result .. self.delimiter .. value) or value
1503 StaticList = class(MultiValue)
1505 function StaticList.__init__(self, ...)
1506 MultiValue.__init__(self, ...)
1508 self.valuelist = self.cfgvalue
1510 if not self.override_scheme
1511 and self.map:get_scheme(self.section.sectiontype, self.option) then
1512 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1513 if self.value and vs.values and not self.override_values then
1514 for k, v in pairs(vs.values) do
1521 function StaticList.validate(self, value)
1522 value = (type(value) == "table") and value or {value}
1525 for i, v in ipairs(value) do
1526 if luci.util.contains(self.keylist, v) then
1527 table.insert(valid, v)
1534 DynamicList = class(AbstractValue)
1536 function DynamicList.__init__(self, ...)
1537 AbstractValue.__init__(self, ...)
1538 self.template = "cbi/dynlist"
1544 function DynamicList.value(self, key, val)
1546 table.insert(self.keylist, tostring(key))
1547 table.insert(self.vallist, tostring(val))
1550 function DynamicList.write(self, ...)
1551 self.map.proceed = true
1552 return AbstractValue.write(self, ...)
1555 function DynamicList.formvalue(self, section)
1556 local value = AbstractValue.formvalue(self, section)
1557 value = (type(value) == "table") and value or {value}
1560 for i, v in ipairs(value) do
1562 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1563 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1564 table.insert(valid, v)
1573 TextValue - A multi-line value
1576 TextValue = class(AbstractValue)
1578 function TextValue.__init__(self, ...)
1579 AbstractValue.__init__(self, ...)
1580 self.template = "cbi/tvalue"
1586 Button = class(AbstractValue)
1588 function Button.__init__(self, ...)
1589 AbstractValue.__init__(self, ...)
1590 self.template = "cbi/button"
1591 self.inputstyle = nil
1596 FileUpload = class(AbstractValue)
1598 function FileUpload.__init__(self, ...)
1599 AbstractValue.__init__(self, ...)
1600 self.template = "cbi/upload"
1601 if not self.map.upload_fields then
1602 self.map.upload_fields = { self }
1604 self.map.upload_fields[#self.map.upload_fields+1] = self
1608 function FileUpload.formcreated(self, section)
1609 return AbstractValue.formcreated(self, section) or
1610 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1611 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1614 function FileUpload.cfgvalue(self, section)
1615 local val = AbstractValue.cfgvalue(self, section)
1616 if val and luci.fs.access(val) then
1622 function FileUpload.formvalue(self, section)
1623 local val = AbstractValue.formvalue(self, section)
1625 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1626 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1636 function FileUpload.remove(self, section)
1637 local val = AbstractValue.formvalue(self, section)
1638 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1639 return AbstractValue.remove(self, section)
1643 FileBrowser = class(AbstractValue)
1645 function FileBrowser.__init__(self, ...)
1646 AbstractValue.__init__(self, ...)
1647 self.template = "cbi/browser"