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
49 CREATE_PREFIX = "cbi.cts."
50 REMOVE_PREFIX = "cbi.rts."
52 -- Loads a CBI map from given file, creating an environment and returns it
53 function load(cbimap, ...)
55 local i18n = require "luci.i18n"
56 require("luci.config")
59 local upldir = "/lib/uci/upload/"
60 local cbidir = luci.util.libpath() .. "/model/cbi/"
62 assert(luci.fs.stat(cbimap) or luci.fs.stat(cbidir..cbimap..".lua"),
65 local func, err = loadfile(cbimap)
67 func, err = loadfile(cbidir..cbimap..".lua")
71 luci.i18n.loadc("cbi")
72 luci.i18n.loadc("uvl")
75 translate=i18n.translate,
76 translatef=i18n.translatef,
80 setfenv(func, setmetatable(env, {__index =
82 return rawget(tbl, key) or _M[key] or _G[key]
85 local maps = { func() }
87 local has_upload = false
89 for i, map in ipairs(maps) do
90 if not instanceof(map, Node) then
91 error("CBI map returns no valid map object!")
95 if map.upload_fields then
97 for _, field in ipairs(map.upload_fields) do
99 field.config .. '.' ..
100 field.section.sectiontype .. '.' ..
109 local uci = luci.model.uci.cursor()
110 local prm = luci.http.context.request.message.params
113 luci.http.setfilehandler(
114 function( field, chunk, eof )
115 if not field then return end
116 if field.name and not cbid then
117 local c, s, o = field.name:gmatch(
118 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
121 if c and s and o then
122 local t = uci:get( c, s )
123 if t and uploads[c.."."..t.."."..o] then
124 local path = upldir .. field.name
125 fd = io.open(path, "w")
134 if field.name == cbid and fd then
150 local function _uvl_validate_section(node, name)
151 local co = node.map:get()
153 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
154 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
156 local function tag_fields(e)
157 if e.option and node.fields[e.option] then
158 if node.fields[e.option].error then
159 node.fields[e.option].error[name] = e
161 node.fields[e.option].error = { [name] = e }
164 for _, c in ipairs(e.childs) do tag_fields(c) end
168 local function tag_section(e)
170 for _, c in ipairs(e.childs or { e }) do
171 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
172 table.insert( s, c.childs[1]:string() )
174 table.insert( s, c:string() )
181 node.error = { [name] = s }
186 local stat, err = node.map.validator:validate_section(node.config, name, co)
188 node.map.save = false
195 local function _uvl_strip_remote_dependencies(deps)
198 for k, v in pairs(deps) do
199 k = k:gsub("%$config%.%$section%.", "")
200 if k:match("^[%w_]+$") and type(v) == "string" then
209 -- Node pseudo abstract class
212 function Node.__init__(self, title, description)
214 self.title = title or ""
215 self.description = description or ""
216 self.template = "cbi/node"
220 function Node._i18n(self, config, section, option, title, description)
223 if type(luci.i18n) == "table" then
225 local key = config and config:gsub("[^%w]+", "") or ""
227 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
228 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
230 self.title = title or luci.i18n.translate( key, option or section or config )
231 self.description = description or luci.i18n.translate( key .. "_desc", "" )
236 function Node.prepare(self, ...)
237 for k, child in ipairs(self.children) do
242 -- Append child nodes
243 function Node.append(self, obj)
244 table.insert(self.children, obj)
247 -- Parse this node and its children
248 function Node.parse(self, ...)
249 for k, child in ipairs(self.children) do
255 function Node.render(self, scope)
259 luci.template.render(self.template, scope)
262 -- Render the children
263 function Node.render_children(self, ...)
264 for k, node in ipairs(self.children) do
271 A simple template element
273 Template = class(Node)
275 function Template.__init__(self, template)
277 self.template = template
280 function Template.render(self)
281 luci.template.render(self.template, {self=self})
286 Map - A map describing a configuration file
290 function Map.__init__(self, config, ...)
291 Node.__init__(self, ...)
292 Node._i18n(self, config, nil, nil, ...)
295 self.parsechain = {self.config}
296 self.template = "cbi/map"
297 self.apply_on_parse = nil
298 self.readinput = true
301 self.uci = uci.cursor()
306 if not self.uci:load(self.config) then
307 error("Unable to read UCI data: " .. self.config)
310 self.validator = luci.uvl.UVL()
311 self.scheme = self.validator:get_scheme(self.config)
315 function Map.formvalue(self, key)
316 return self.readinput and luci.http.formvalue(key)
319 function Map.formvaluetable(self, key)
320 return self.readinput and luci.http.formvaluetable(key)
323 function Map.get_scheme(self, sectiontype, option)
325 return self.scheme and self.scheme.sections[sectiontype]
327 return self.scheme and self.scheme.variables[sectiontype]
328 and self.scheme.variables[sectiontype][option]
332 function Map.submitstate(self)
333 return self:formvalue("cbi.submit")
336 -- Chain foreign config
337 function Map.chain(self, config)
338 table.insert(self.parsechain, config)
341 function Map.state_handler(self, state)
345 -- Use optimized UCI writing
346 function Map.parse(self, readinput, ...)
347 self.readinput = (readinput ~= false)
349 if self:formvalue("cbi.skip") then
350 self.state = FORM_SKIP
351 return self:state_handler(self.state)
354 Node.parse(self, ...)
357 for i, config in ipairs(self.parsechain) do
358 self.uci:save(config)
360 if self:submitstate() and not self.proceed and (self.flow.autoapply or luci.http.formvalue("cbi.apply")) then
361 for i, config in ipairs(self.parsechain) do
362 self.uci:commit(config)
364 -- Refresh data because commit changes section names
365 self.uci:load(config)
367 if self.apply_on_parse then
368 self.uci:apply(self.parsechain)
370 self._apply = function()
371 local cmd = self.uci:apply(self.parsechain, true)
377 Node.parse(self, true)
380 for i, config in ipairs(self.parsechain) do
381 self.uci:unload(config)
383 if type(self.commit_handler) == "function" then
384 self:commit_handler(self:submitstate())
388 if self:submitstate() then
389 if not self.save then
390 self.state = FORM_INVALID
391 elseif self.proceed then
392 self.state = FORM_PROCEED
394 self.state = self.changed and FORM_CHANGED or FORM_VALID
397 self.state = FORM_NODATA
400 return self:state_handler(self.state)
403 function Map.render(self, ...)
404 Node.render(self, ...)
406 local fp = self._apply()
412 -- Creates a child section
413 function Map.section(self, class, ...)
414 if instanceof(class, AbstractSection) then
415 local obj = class(self, ...)
419 error("class must be a descendent of AbstractSection")
424 function Map.add(self, sectiontype)
425 return self.uci:add(self.config, sectiontype)
429 function Map.set(self, section, option, value)
431 return self.uci:set(self.config, section, option, value)
433 return self.uci:set(self.config, section, value)
438 function Map.del(self, section, option)
440 return self.uci:delete(self.config, section, option)
442 return self.uci:delete(self.config, section)
447 function Map.get(self, section, option)
449 return self.uci:get_all(self.config)
451 return self.uci:get(self.config, section, option)
453 return self.uci:get_all(self.config, section)
460 Compound = class(Node)
462 function Compound.__init__(self, ...)
464 self.children = {...}
467 function Compound.parse(self, ...)
468 local cstate, state = 0, 0
470 for k, child in ipairs(self.children) do
471 cstate = child:parse(...)
472 state = (not state or cstate < state) and cstate or state
480 Delegator - Node controller
482 Delegator = class(Node)
483 function Delegator.__init__(self, ...)
484 Node.__init__(self, ...)
486 self.template = "cbi/delegator"
489 function Delegator.state(self, name, node, transitor)
490 transitor = transitor or self.transistor_linear
491 local state = {node=node, name=name, transitor=transitor}
493 assert(instanceof(node, Node), "Invalid node")
494 assert(not self.nodes[name], "Duplicate entry")
496 self.nodes[name] = state
502 function Delegator.get(self, name)
503 return self.nodes[name]
506 function Delegator.transistor_linear(self, state, cstate)
508 for i, child in ipairs(self.children) do
509 if state == child then
510 return self.children[i+1]
518 function Delegator.parse(self, ...)
519 local active = self:getactive()
520 assert(active, "Invalid state")
522 local cstate = active.node:parse()
523 self.active = active.transistor(self, active.node, cstate)
525 if not self.active then
528 self.active:parse(false)
533 function Delegator.render(self, ...)
534 self.active.node:render(...)
537 function Delegator.getactive(self)
538 return self:get(Map.formvalue(self, "cbi.delegated")
539 or (self.children[1] and self.children[1].name))
547 Page.__init__ = Node.__init__
548 Page.parse = function() end
552 SimpleForm - A Simple non-UCI form
554 SimpleForm = class(Node)
556 function SimpleForm.__init__(self, config, title, description, data)
557 Node.__init__(self, title, description)
559 self.data = data or {}
560 self.template = "cbi/simpleform"
562 self.pageaction = false
563 self.readinput = true
566 SimpleForm.formvalue = Map.formvalue
567 SimpleForm.formvaluetable = Map.formvaluetable
569 function SimpleForm.parse(self, readinput, ...)
570 self.readinput = (readinput ~= false)
572 if self:formvalue("cbi.skip") then
576 if self:submitstate() then
577 Node.parse(self, 1, ...)
581 for k, j in ipairs(self.children) do
582 for i, v in ipairs(j.children) do
584 and (not v.tag_missing or not v.tag_missing[1])
585 and (not v.tag_invalid or not v.tag_invalid[1])
591 not self:submitstate() and FORM_NODATA
592 or valid and FORM_VALID
595 self.dorender = not self.handle
597 local nrender, nstate = self:handle(state, self.data)
598 self.dorender = self.dorender or (nrender ~= false)
599 state = nstate or state
604 function SimpleForm.render(self, ...)
605 if self.dorender then
606 Node.render(self, ...)
610 function SimpleForm.submitstate(self)
611 return self:formvalue("cbi.submit")
614 function SimpleForm.section(self, class, ...)
615 if instanceof(class, AbstractSection) then
616 local obj = class(self, ...)
620 error("class must be a descendent of AbstractSection")
624 -- Creates a child field
625 function SimpleForm.field(self, class, ...)
627 for k, v in ipairs(self.children) do
628 if instanceof(v, SimpleSection) then
634 section = self:section(SimpleSection)
637 if instanceof(class, AbstractValue) then
638 local obj = class(self, section, ...)
639 obj.track_missing = true
643 error("class must be a descendent of AbstractValue")
647 function SimpleForm.set(self, section, option, value)
648 self.data[option] = value
652 function SimpleForm.del(self, section, option)
653 self.data[option] = nil
657 function SimpleForm.get(self, section, option)
658 return self.data[option]
662 function SimpleForm.get_scheme()
667 Form = class(SimpleForm)
669 function Form.__init__(self, ...)
670 SimpleForm.__init__(self, ...)
678 AbstractSection = class(Node)
680 function AbstractSection.__init__(self, map, sectiontype, ...)
681 Node.__init__(self, ...)
682 self.sectiontype = sectiontype
684 self.config = map.config
689 self.tag_invalid = {}
690 self.tag_deperror = {}
694 self.addremove = false
698 -- Appends a new option
699 function AbstractSection.option(self, class, option, ...)
700 -- Autodetect from UVL
701 if class == true and self.map:get_scheme(self.sectiontype, option) then
702 local vs = self.map:get_scheme(self.sectiontype, option)
703 if vs.type == "boolean" then
705 elseif vs.type == "list" then
707 elseif vs.type == "enum" or vs.type == "reference" then
714 if instanceof(class, AbstractValue) then
715 local obj = class(self.map, self, option, ...)
717 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
720 self.fields[option] = obj
722 elseif class == true then
723 error("No valid class was given and autodetection failed.")
725 error("class must be a descendant of AbstractValue")
729 -- Parse optional options
730 function AbstractSection.parse_optionals(self, section)
731 if not self.optional then
735 self.optionals[section] = {}
737 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
738 for k,v in ipairs(self.children) do
739 if v.optional and not v:cfgvalue(section) then
740 if field == v.option then
743 self.map.proceed = true
744 table.insert(self.optionals[section], v)
749 if field and #field > 0 and self.dynamic then
750 self:add_dynamic(field)
754 -- Add a dynamic option
755 function AbstractSection.add_dynamic(self, field, optional)
756 local o = self:option(Value, field, field)
757 o.optional = optional
760 -- Parse all dynamic options
761 function AbstractSection.parse_dynamic(self, section)
762 if not self.dynamic then
766 local arr = luci.util.clone(self:cfgvalue(section))
767 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
768 for k, v in pairs(form) do
772 for key,val in pairs(arr) do
775 for i,c in ipairs(self.children) do
776 if c.option == key then
781 if create and key:sub(1, 1) ~= "." then
782 self.map.proceed = true
783 self:add_dynamic(key, true)
788 -- Returns the section's UCI table
789 function AbstractSection.cfgvalue(self, section)
790 return self.map:get(section)
794 function AbstractSection.push_events(self)
795 --luci.util.append(self.map.events, self.events)
796 self.map.changed = true
799 -- Removes the section
800 function AbstractSection.remove(self, section)
801 self.map.proceed = true
802 return self.map:del(section)
805 -- Creates the section
806 function AbstractSection.create(self, section)
810 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
812 section = self.map:add(self.sectiontype)
817 for k,v in pairs(self.children) do
819 self.map:set(section, v.option, v.default)
823 for k,v in pairs(self.defaults) do
824 self.map:set(section, k, v)
828 self.map.proceed = true
834 SimpleSection = class(AbstractSection)
836 function SimpleSection.__init__(self, form, ...)
837 AbstractSection.__init__(self, form, nil, ...)
838 self.template = "cbi/nullsection"
842 Table = class(AbstractSection)
844 function Table.__init__(self, form, data, ...)
845 local datasource = {}
847 datasource.config = "table"
848 self.data = data or {}
850 datasource.formvalue = Map.formvalue
851 datasource.formvaluetable = Map.formvaluetable
852 datasource.readinput = true
854 function datasource.get(self, section, option)
855 return tself.data[section] and tself.data[section][option]
858 function datasource.submitstate(self)
859 return Map.formvalue(self, "cbi.submit")
862 function datasource.del(...)
866 function datasource.get_scheme()
870 AbstractSection.__init__(self, datasource, "table", ...)
871 self.template = "cbi/tblsection"
872 self.rowcolors = true
873 self.anonymous = true
876 function Table.parse(self, readinput)
877 self.map.readinput = (readinput ~= false)
878 for i, k in ipairs(self:cfgsections()) do
879 if self.map:submitstate() then
885 function Table.cfgsections(self)
888 for i, v in luci.util.kspairs(self.data) do
889 table.insert(sections, i)
895 function Table.update(self, data)
902 NamedSection - A fixed configuration section defined by its name
904 NamedSection = class(AbstractSection)
906 function NamedSection.__init__(self, map, section, stype, ...)
907 AbstractSection.__init__(self, map, stype, ...)
908 Node._i18n(self, map.config, section, nil, ...)
911 self.addremove = false
913 -- Use defaults from UVL
914 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
915 local vs = self.map:get_scheme(self.sectiontype)
916 self.addremove = not vs.unique and not vs.required
917 self.dynamic = vs.dynamic
918 self.title = self.title or vs.title
919 self.description = self.description or vs.descr
922 self.template = "cbi/nsection"
923 self.section = section
926 function NamedSection.parse(self, novld)
927 local s = self.section
928 local active = self:cfgvalue(s)
930 if self.addremove then
931 local path = self.config.."."..s
932 if active then -- Remove the section
933 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
937 else -- Create and apply default values
938 if self.map:formvalue("cbi.cns."..path) then
946 AbstractSection.parse_dynamic(self, s)
947 if self.map:submitstate() then
950 if not novld and not self.override_scheme and self.map.scheme then
951 _uvl_validate_section(self, s)
954 AbstractSection.parse_optionals(self, s)
964 TypedSection - A (set of) configuration section(s) defined by the type
965 addremove: Defines whether the user can add/remove sections of this type
966 anonymous: Allow creating anonymous sections
967 validate: a validation function returning nil if the section is invalid
969 TypedSection = class(AbstractSection)
971 function TypedSection.__init__(self, map, type, ...)
972 AbstractSection.__init__(self, map, type, ...)
973 Node._i18n(self, map.config, type, nil, ...)
975 self.template = "cbi/tsection"
977 self.anonymous = false
979 -- Use defaults from UVL
980 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
981 local vs = self.map:get_scheme(self.sectiontype)
982 self.addremove = not vs.unique and not vs.required
983 self.dynamic = vs.dynamic
984 self.anonymous = not vs.named
985 self.title = self.title or vs.title
986 self.description = self.description or vs.descr
990 -- Return all matching UCI sections for this TypedSection
991 function TypedSection.cfgsections(self)
993 self.map.uci:foreach(self.map.config, self.sectiontype,
995 if self:checkscope(section[".name"]) then
996 table.insert(sections, section[".name"])
1003 -- Limits scope to sections that have certain option => value pairs
1004 function TypedSection.depends(self, option, value)
1005 table.insert(self.deps, {option=option, value=value})
1008 function TypedSection.parse(self, novld)
1009 if self.addremove then
1011 local crval = REMOVE_PREFIX .. self.config
1012 local name = self.map:formvaluetable(crval)
1013 for k,v in pairs(name) do
1014 if k:sub(-2) == ".x" then
1015 k = k:sub(1, #k - 2)
1017 if self:cfgvalue(k) and self:checkscope(k) then
1024 for i, k in ipairs(self:cfgsections()) do
1025 AbstractSection.parse_dynamic(self, k)
1026 if self.map:submitstate() then
1027 Node.parse(self, k, novld)
1029 if not novld and not self.override_scheme and self.map.scheme then
1030 _uvl_validate_section(self, k)
1033 AbstractSection.parse_optionals(self, k)
1036 if self.addremove then
1039 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1040 local name = self.map:formvalue(crval)
1041 if self.anonymous then
1043 created = self:create()
1047 -- Ignore if it already exists
1048 if self:cfgvalue(name) then
1052 name = self:checkscope(name)
1055 self.err_invalid = true
1058 if name and #name > 0 then
1059 created = self:create(name) and name
1061 self.invalid_cts = true
1068 AbstractSection.parse_optionals(self, created)
1072 if created or self.changed then
1077 -- Verifies scope of sections
1078 function TypedSection.checkscope(self, section)
1079 -- Check if we are not excluded
1080 if self.filter and not self:filter(section) then
1084 -- Check if at least one dependency is met
1085 if #self.deps > 0 and self:cfgvalue(section) then
1088 for k, v in ipairs(self.deps) do
1089 if self:cfgvalue(section)[v.option] == v.value then
1099 return self:validate(section)
1103 -- Dummy validate function
1104 function TypedSection.validate(self, section)
1110 AbstractValue - An abstract Value Type
1111 null: Value can be empty
1112 valid: A function returning the value if it is valid otherwise nil
1113 depends: A table of option => value pairs of which one must be true
1114 default: The default value
1115 size: The size of the input fields
1116 rmempty: Unset value if empty
1117 optional: This value is optional (see AbstractSection.optionals)
1119 AbstractValue = class(Node)
1121 function AbstractValue.__init__(self, map, section, option, ...)
1122 Node.__init__(self, ...)
1123 self.section = section
1124 self.option = option
1126 self.config = map.config
1127 self.tag_invalid = {}
1128 self.tag_missing = {}
1129 self.tag_reqerror = {}
1132 --self.cast = "string"
1134 self.track_missing = false
1138 self.optional = false
1141 function AbstractValue.prepare(self)
1142 -- Use defaults from UVL
1143 if not self.override_scheme
1144 and self.map:get_scheme(self.section.sectiontype, self.option) then
1145 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1146 if self.cast == nil then
1147 self.cast = (vs.type == "list") and "list" or "string"
1149 self.title = self.title or vs.title
1150 self.description = self.description or vs.descr
1151 if self.default == nil then
1152 self.default = vs.default
1155 if vs.depends and not self.override_dependencies then
1156 for i, deps in ipairs(vs.depends) do
1157 deps = _uvl_strip_remote_dependencies(deps)
1165 self.cast = self.cast or "string"
1168 -- Add a dependencie to another section field
1169 function AbstractValue.depends(self, field, value)
1171 if type(field) == "string" then
1178 table.insert(self.deps, {deps=deps, add=""})
1181 -- Generates the unique CBID
1182 function AbstractValue.cbid(self, section)
1183 return "cbid."..self.map.config.."."..section.."."..self.option
1186 -- Return whether this object should be created
1187 function AbstractValue.formcreated(self, section)
1188 local key = "cbi.opt."..self.config.."."..section
1189 return (self.map:formvalue(key) == self.option)
1192 -- Returns the formvalue for this object
1193 function AbstractValue.formvalue(self, section)
1194 return self.map:formvalue(self:cbid(section))
1197 function AbstractValue.additional(self, value)
1198 self.optional = value
1201 function AbstractValue.mandatory(self, value)
1202 self.rmempty = not value
1205 function AbstractValue.parse(self, section, novld)
1206 local fvalue = self:formvalue(section)
1207 local cvalue = self:cfgvalue(section)
1209 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1210 fvalue = self:transform(self:validate(fvalue, section))
1211 if not fvalue and not novld then
1213 self.error[section] = "invalid"
1215 self.error = { [section] = "invalid" }
1217 self.map.save = false
1219 if fvalue and not (fvalue == cvalue) then
1220 if self:write(section, fvalue) then
1222 self.section.changed = true
1223 --luci.util.append(self.map.events, self.events)
1226 else -- Unset the UCI or error
1227 if self.rmempty or self.optional then
1228 if self:remove(section) then
1230 self.section.changed = true
1231 --luci.util.append(self.map.events, self.events)
1233 elseif cvalue ~= fvalue and not novld then
1234 self:write(section, fvalue or "")
1236 self.error[section] = "missing"
1238 self.error = { [section] = "missing" }
1240 self.map.save = false
1245 -- Render if this value exists or if it is mandatory
1246 function AbstractValue.render(self, s, scope)
1247 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1250 scope.cbid = self:cbid(s)
1251 scope.striptags = luci.util.striptags
1253 scope.ifattr = function(cond,key,val)
1255 return string.format(
1256 ' %s="%s"', tostring(key),
1257 luci.util.pcdata(tostring( val
1259 or (type(self[key]) ~= "function" and self[key])
1267 scope.attr = function(...)
1268 return scope.ifattr( true, ... )
1271 Node.render(self, scope)
1275 -- Return the UCI value of this object
1276 function AbstractValue.cfgvalue(self, section)
1277 local value = self.map:get(section, self.option)
1280 elseif not self.cast or self.cast == type(value) then
1282 elseif self.cast == "string" then
1283 if type(value) == "table" then
1286 elseif self.cast == "table" then
1287 return luci.util.split(value, "%s+", nil, true)
1291 -- Validate the form value
1292 function AbstractValue.validate(self, value)
1296 AbstractValue.transform = AbstractValue.validate
1300 function AbstractValue.write(self, section, value)
1301 return self.map:set(section, self.option, value)
1305 function AbstractValue.remove(self, section)
1306 return self.map:del(section, self.option)
1313 Value - A one-line value
1314 maxlength: The maximum length
1316 Value = class(AbstractValue)
1318 function Value.__init__(self, ...)
1319 AbstractValue.__init__(self, ...)
1320 self.template = "cbi/value"
1325 function Value.value(self, key, val)
1327 table.insert(self.keylist, tostring(key))
1328 table.insert(self.vallist, tostring(val))
1332 -- DummyValue - This does nothing except being there
1333 DummyValue = class(AbstractValue)
1335 function DummyValue.__init__(self, ...)
1336 AbstractValue.__init__(self, ...)
1337 self.template = "cbi/dvalue"
1341 function DummyValue.cfgvalue(self, section)
1344 if type(self.value) == "function" then
1345 value = self:value(section)
1350 value = AbstractValue.cfgvalue(self, section)
1355 function DummyValue.parse(self)
1361 Flag - A flag being enabled or disabled
1363 Flag = class(AbstractValue)
1365 function Flag.__init__(self, ...)
1366 AbstractValue.__init__(self, ...)
1367 self.template = "cbi/fvalue"
1373 -- A flag can only have two states: set or unset
1374 function Flag.parse(self, section)
1375 local fvalue = self:formvalue(section)
1378 fvalue = self.enabled
1380 fvalue = self.disabled
1383 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1384 if not(fvalue == self:cfgvalue(section)) then
1385 self:write(section, fvalue)
1388 self:remove(section)
1395 ListValue - A one-line value predefined in a list
1396 widget: The widget that will be used (select, radio)
1398 ListValue = class(AbstractValue)
1400 function ListValue.__init__(self, ...)
1401 AbstractValue.__init__(self, ...)
1402 self.template = "cbi/lvalue"
1407 self.widget = "select"
1410 function ListValue.prepare(self, ...)
1411 AbstractValue.prepare(self, ...)
1412 if not self.override_scheme
1413 and self.map:get_scheme(self.section.sectiontype, self.option) then
1414 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1415 if self.value and vs.valuelist and not self.override_values then
1416 for k, v in ipairs(vs.valuelist) do
1418 if not self.override_dependencies
1419 and vs.enum_depends and vs.enum_depends[v.value] then
1420 for i, dep in ipairs(vs.enum_depends[v.value]) do
1421 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1424 self:value(v.value, v.title or v.value, unpack(deps))
1430 function ListValue.value(self, key, val, ...)
1431 if luci.util.contains(self.keylist, key) then
1436 table.insert(self.keylist, tostring(key))
1437 table.insert(self.vallist, tostring(val))
1439 for i, deps in ipairs({...}) do
1440 table.insert(self.deps, {add = "-"..key, deps=deps})
1444 function ListValue.validate(self, val)
1445 if luci.util.contains(self.keylist, val) then
1455 MultiValue - Multiple delimited values
1456 widget: The widget that will be used (select, checkbox)
1457 delimiter: The delimiter that will separate the values (default: " ")
1459 MultiValue = class(AbstractValue)
1461 function MultiValue.__init__(self, ...)
1462 AbstractValue.__init__(self, ...)
1463 self.template = "cbi/mvalue"
1468 self.widget = "checkbox"
1469 self.delimiter = " "
1472 function MultiValue.render(self, ...)
1473 if self.widget == "select" and not self.size then
1474 self.size = #self.vallist
1477 AbstractValue.render(self, ...)
1480 function MultiValue.value(self, key, val)
1481 if luci.util.contains(self.keylist, key) then
1486 table.insert(self.keylist, tostring(key))
1487 table.insert(self.vallist, tostring(val))
1490 function MultiValue.valuelist(self, section)
1491 local val = self:cfgvalue(section)
1493 if not(type(val) == "string") then
1497 return luci.util.split(val, self.delimiter)
1500 function MultiValue.validate(self, val)
1501 val = (type(val) == "table") and val or {val}
1505 for i, value in ipairs(val) do
1506 if luci.util.contains(self.keylist, value) then
1507 result = result and (result .. self.delimiter .. value) or value
1515 StaticList = class(MultiValue)
1517 function StaticList.__init__(self, ...)
1518 MultiValue.__init__(self, ...)
1520 self.valuelist = self.cfgvalue
1522 if not self.override_scheme
1523 and self.map:get_scheme(self.section.sectiontype, self.option) then
1524 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1525 if self.value and vs.values and not self.override_values then
1526 for k, v in pairs(vs.values) do
1533 function StaticList.validate(self, value)
1534 value = (type(value) == "table") and value or {value}
1537 for i, v in ipairs(value) do
1538 if luci.util.contains(self.keylist, v) then
1539 table.insert(valid, v)
1546 DynamicList = class(AbstractValue)
1548 function DynamicList.__init__(self, ...)
1549 AbstractValue.__init__(self, ...)
1550 self.template = "cbi/dynlist"
1556 function DynamicList.value(self, key, val)
1558 table.insert(self.keylist, tostring(key))
1559 table.insert(self.vallist, tostring(val))
1562 function DynamicList.write(self, ...)
1563 self.map.proceed = true
1564 return AbstractValue.write(self, ...)
1567 function DynamicList.formvalue(self, section)
1568 local value = AbstractValue.formvalue(self, section)
1569 value = (type(value) == "table") and value or {value}
1572 for i, v in ipairs(value) do
1574 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1575 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1576 table.insert(valid, v)
1585 TextValue - A multi-line value
1588 TextValue = class(AbstractValue)
1590 function TextValue.__init__(self, ...)
1591 AbstractValue.__init__(self, ...)
1592 self.template = "cbi/tvalue"
1598 Button = class(AbstractValue)
1600 function Button.__init__(self, ...)
1601 AbstractValue.__init__(self, ...)
1602 self.template = "cbi/button"
1603 self.inputstyle = nil
1608 FileUpload = class(AbstractValue)
1610 function FileUpload.__init__(self, ...)
1611 AbstractValue.__init__(self, ...)
1612 self.template = "cbi/upload"
1613 if not self.map.upload_fields then
1614 self.map.upload_fields = { self }
1616 self.map.upload_fields[#self.map.upload_fields+1] = self
1620 function FileUpload.formcreated(self, section)
1621 return AbstractValue.formcreated(self, section) or
1622 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1623 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1626 function FileUpload.cfgvalue(self, section)
1627 local val = AbstractValue.cfgvalue(self, section)
1628 if val and luci.fs.access(val) then
1634 function FileUpload.formvalue(self, section)
1635 local val = AbstractValue.formvalue(self, section)
1637 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1638 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1648 function FileUpload.remove(self, section)
1649 local val = AbstractValue.formvalue(self, section)
1650 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1651 return AbstractValue.remove(self, section)
1655 FileBrowser = class(AbstractValue)
1657 function FileBrowser.__init__(self, ...)
1658 AbstractValue.__init__(self, ...)
1659 self.template = "cbi/browser"