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
63 luci.fs.stat(cbidir..cbimap..".lua") or
64 luci.fs.stat(cbidir..cbimap..".lua.gz"),
67 local func, err = loadfile(cbimap)
69 func, err = loadfile(cbidir..cbimap..".lua") or
70 loadfile(cbidir..cbimap..".lua.gz")
74 luci.i18n.loadc("cbi")
75 luci.i18n.loadc("uvl")
78 translate=i18n.translate,
79 translatef=i18n.translatef,
83 setfenv(func, setmetatable(env, {__index =
85 return rawget(tbl, key) or _M[key] or _G[key]
88 local maps = { func() }
90 local has_upload = false
92 for i, map in ipairs(maps) do
93 if not instanceof(map, Node) then
94 error("CBI map returns no valid map object!")
98 if map.upload_fields then
100 for _, field in ipairs(map.upload_fields) do
102 field.config .. '.' ..
103 field.section.sectiontype .. '.' ..
112 local uci = luci.model.uci.cursor()
113 local prm = luci.http.context.request.message.params
116 luci.http.setfilehandler(
117 function( field, chunk, eof )
118 if not field then return end
119 if field.name and not cbid then
120 local c, s, o = field.name:gmatch(
121 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
124 if c and s and o then
125 local t = uci:get( c, s )
126 if t and uploads[c.."."..t.."."..o] then
127 local path = upldir .. field.name
128 fd = io.open(path, "w")
137 if field.name == cbid and fd then
153 local function _uvl_validate_section(node, name)
154 local co = node.map:get()
156 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
157 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
159 local function tag_fields(e)
160 if e.option and node.fields[e.option] then
161 if node.fields[e.option].error then
162 node.fields[e.option].error[name] = e
164 node.fields[e.option].error = { [name] = e }
167 for _, c in ipairs(e.childs) do tag_fields(c) end
171 local function tag_section(e)
173 for _, c in ipairs(e.childs or { e }) do
174 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
175 table.insert( s, c.childs[1]:string() )
177 table.insert( s, c:string() )
184 node.error = { [name] = s }
189 local stat, err = node.map.validator:validate_section(node.config, name, co)
191 node.map.save = false
198 local function _uvl_strip_remote_dependencies(deps)
201 for k, v in pairs(deps) do
202 k = k:gsub("%$config%.%$section%.", "")
203 if k:match("^[%w_]+$") and type(v) == "string" then
212 -- Node pseudo abstract class
215 function Node.__init__(self, title, description)
217 self.title = title or ""
218 self.description = description or ""
219 self.template = "cbi/node"
223 function Node._i18n(self, config, section, option, title, description)
226 if type(luci.i18n) == "table" then
228 local key = config and config:gsub("[^%w]+", "") or ""
230 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
231 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
233 self.title = title or luci.i18n.translate( key, option or section or config )
234 self.description = description or luci.i18n.translate( key .. "_desc", "" )
239 function Node.prepare(self, ...)
240 for k, child in ipairs(self.children) do
245 -- Append child nodes
246 function Node.append(self, obj)
247 table.insert(self.children, obj)
250 -- Parse this node and its children
251 function Node.parse(self, ...)
252 for k, child in ipairs(self.children) do
258 function Node.render(self, scope)
262 luci.template.render(self.template, scope)
265 -- Render the children
266 function Node.render_children(self, ...)
267 for k, node in ipairs(self.children) do
274 A simple template element
276 Template = class(Node)
278 function Template.__init__(self, template)
280 self.template = template
283 function Template.render(self)
284 luci.template.render(self.template, {self=self})
289 Map - A map describing a configuration file
293 function Map.__init__(self, config, ...)
294 Node.__init__(self, ...)
295 Node._i18n(self, config, nil, nil, ...)
298 self.parsechain = {self.config}
299 self.template = "cbi/map"
300 self.apply_on_parse = nil
301 self.readinput = true
304 self.uci = uci.cursor()
309 if not self.uci:load(self.config) then
310 error("Unable to read UCI data: " .. self.config)
313 self.validator = luci.uvl.UVL()
314 self.scheme = self.validator:get_scheme(self.config)
318 function Map.formvalue(self, key)
319 return self.readinput and luci.http.formvalue(key)
322 function Map.formvaluetable(self, key)
323 return self.readinput and luci.http.formvaluetable(key)
326 function Map.get_scheme(self, sectiontype, option)
328 return self.scheme and self.scheme.sections[sectiontype]
330 return self.scheme and self.scheme.variables[sectiontype]
331 and self.scheme.variables[sectiontype][option]
335 function Map.submitstate(self)
336 return self:formvalue("cbi.submit")
339 -- Chain foreign config
340 function Map.chain(self, config)
341 table.insert(self.parsechain, config)
344 function Map.state_handler(self, state)
348 -- Use optimized UCI writing
349 function Map.parse(self, readinput, ...)
350 self.readinput = (readinput ~= false)
352 if self:formvalue("cbi.skip") then
353 self.state = FORM_SKIP
354 return self:state_handler(self.state)
357 Node.parse(self, ...)
360 for i, config in ipairs(self.parsechain) do
361 self.uci:save(config)
363 if self:submitstate() and not self.proceed and (self.flow.autoapply or luci.http.formvalue("cbi.apply")) then
364 for i, config in ipairs(self.parsechain) do
365 self.uci:commit(config)
367 -- Refresh data because commit changes section names
368 self.uci:load(config)
370 if self.apply_on_parse then
371 self.uci:apply(self.parsechain)
373 self._apply = function()
374 local cmd = self.uci:apply(self.parsechain, true)
380 Node.parse(self, true)
383 for i, config in ipairs(self.parsechain) do
384 self.uci:unload(config)
386 if type(self.commit_handler) == "function" then
387 self:commit_handler(self:submitstate())
391 if self:submitstate() then
392 if not self.save then
393 self.state = FORM_INVALID
394 elseif self.proceed then
395 self.state = FORM_PROCEED
397 self.state = self.changed and FORM_CHANGED or FORM_VALID
400 self.state = FORM_NODATA
403 return self:state_handler(self.state)
406 function Map.render(self, ...)
407 Node.render(self, ...)
409 local fp = self._apply()
415 -- Creates a child section
416 function Map.section(self, class, ...)
417 if instanceof(class, AbstractSection) then
418 local obj = class(self, ...)
422 error("class must be a descendent of AbstractSection")
427 function Map.add(self, sectiontype)
428 return self.uci:add(self.config, sectiontype)
432 function Map.set(self, section, option, value)
434 return self.uci:set(self.config, section, option, value)
436 return self.uci:set(self.config, section, value)
441 function Map.del(self, section, option)
443 return self.uci:delete(self.config, section, option)
445 return self.uci:delete(self.config, section)
450 function Map.get(self, section, option)
452 return self.uci:get_all(self.config)
454 return self.uci:get(self.config, section, option)
456 return self.uci:get_all(self.config, section)
463 Compound = class(Node)
465 function Compound.__init__(self, ...)
467 self.children = {...}
470 function Compound.parse(self, ...)
471 local cstate, state = 0, 0
473 for k, child in ipairs(self.children) do
474 cstate = child:parse(...)
475 state = (not state or cstate < state) and cstate or state
483 Delegator - Node controller
485 Delegator = class(Node)
486 function Delegator.__init__(self, ...)
487 Node.__init__(self, ...)
489 self.template = "cbi/delegator"
492 function Delegator.state(self, name, node, transitor)
493 transitor = transitor or self.transistor_linear
494 local state = {node=node, name=name, transitor=transitor}
496 assert(instanceof(node, Node), "Invalid node")
497 assert(not self.nodes[name], "Duplicate entry")
499 self.nodes[name] = state
505 function Delegator.get(self, name)
506 return self.nodes[name]
509 function Delegator.transistor_linear(self, state, cstate)
511 for i, child in ipairs(self.children) do
512 if state == child then
513 return self.children[i+1]
521 function Delegator.parse(self, ...)
522 local active = self:getactive()
523 assert(active, "Invalid state")
525 local cstate = active.node:parse()
526 self.active = active.transistor(self, active.node, cstate)
528 if not self.active then
531 self.active:parse(false)
536 function Delegator.render(self, ...)
537 self.active.node:render(...)
540 function Delegator.getactive(self)
541 return self:get(Map.formvalue(self, "cbi.delegated")
542 or (self.children[1] and self.children[1].name))
550 Page.__init__ = Node.__init__
551 Page.parse = function() end
555 SimpleForm - A Simple non-UCI form
557 SimpleForm = class(Node)
559 function SimpleForm.__init__(self, config, title, description, data)
560 Node.__init__(self, title, description)
562 self.data = data or {}
563 self.template = "cbi/simpleform"
565 self.pageaction = false
566 self.readinput = true
569 SimpleForm.formvalue = Map.formvalue
570 SimpleForm.formvaluetable = Map.formvaluetable
572 function SimpleForm.parse(self, readinput, ...)
573 self.readinput = (readinput ~= false)
575 if self:formvalue("cbi.skip") then
579 if self:submitstate() then
580 Node.parse(self, 1, ...)
584 for k, j in ipairs(self.children) do
585 for i, v in ipairs(j.children) do
587 and (not v.tag_missing or not v.tag_missing[1])
588 and (not v.tag_invalid or not v.tag_invalid[1])
594 not self:submitstate() and FORM_NODATA
595 or valid and FORM_VALID
598 self.dorender = not self.handle
600 local nrender, nstate = self:handle(state, self.data)
601 self.dorender = self.dorender or (nrender ~= false)
602 state = nstate or state
607 function SimpleForm.render(self, ...)
608 if self.dorender then
609 Node.render(self, ...)
613 function SimpleForm.submitstate(self)
614 return self:formvalue("cbi.submit")
617 function SimpleForm.section(self, class, ...)
618 if instanceof(class, AbstractSection) then
619 local obj = class(self, ...)
623 error("class must be a descendent of AbstractSection")
627 -- Creates a child field
628 function SimpleForm.field(self, class, ...)
630 for k, v in ipairs(self.children) do
631 if instanceof(v, SimpleSection) then
637 section = self:section(SimpleSection)
640 if instanceof(class, AbstractValue) then
641 local obj = class(self, section, ...)
642 obj.track_missing = true
646 error("class must be a descendent of AbstractValue")
650 function SimpleForm.set(self, section, option, value)
651 self.data[option] = value
655 function SimpleForm.del(self, section, option)
656 self.data[option] = nil
660 function SimpleForm.get(self, section, option)
661 return self.data[option]
665 function SimpleForm.get_scheme()
670 Form = class(SimpleForm)
672 function Form.__init__(self, ...)
673 SimpleForm.__init__(self, ...)
681 AbstractSection = class(Node)
683 function AbstractSection.__init__(self, map, sectiontype, ...)
684 Node.__init__(self, ...)
685 self.sectiontype = sectiontype
687 self.config = map.config
692 self.tag_invalid = {}
693 self.tag_deperror = {}
697 self.addremove = false
701 -- Appends a new option
702 function AbstractSection.option(self, class, option, ...)
703 -- Autodetect from UVL
704 if class == true and self.map:get_scheme(self.sectiontype, option) then
705 local vs = self.map:get_scheme(self.sectiontype, option)
706 if vs.type == "boolean" then
708 elseif vs.type == "list" then
710 elseif vs.type == "enum" or vs.type == "reference" then
717 if instanceof(class, AbstractValue) then
718 local obj = class(self.map, self, option, ...)
720 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
723 self.fields[option] = obj
725 elseif class == true then
726 error("No valid class was given and autodetection failed.")
728 error("class must be a descendant of AbstractValue")
732 -- Parse optional options
733 function AbstractSection.parse_optionals(self, section)
734 if not self.optional then
738 self.optionals[section] = {}
740 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
741 for k,v in ipairs(self.children) do
742 if v.optional and not v:cfgvalue(section) then
743 if field == v.option then
746 self.map.proceed = true
747 table.insert(self.optionals[section], v)
752 if field and #field > 0 and self.dynamic then
753 self:add_dynamic(field)
757 -- Add a dynamic option
758 function AbstractSection.add_dynamic(self, field, optional)
759 local o = self:option(Value, field, field)
760 o.optional = optional
763 -- Parse all dynamic options
764 function AbstractSection.parse_dynamic(self, section)
765 if not self.dynamic then
769 local arr = luci.util.clone(self:cfgvalue(section))
770 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
771 for k, v in pairs(form) do
775 for key,val in pairs(arr) do
778 for i,c in ipairs(self.children) do
779 if c.option == key then
784 if create and key:sub(1, 1) ~= "." then
785 self.map.proceed = true
786 self:add_dynamic(key, true)
791 -- Returns the section's UCI table
792 function AbstractSection.cfgvalue(self, section)
793 return self.map:get(section)
797 function AbstractSection.push_events(self)
798 --luci.util.append(self.map.events, self.events)
799 self.map.changed = true
802 -- Removes the section
803 function AbstractSection.remove(self, section)
804 self.map.proceed = true
805 return self.map:del(section)
808 -- Creates the section
809 function AbstractSection.create(self, section)
813 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
815 section = self.map:add(self.sectiontype)
820 for k,v in pairs(self.children) do
822 self.map:set(section, v.option, v.default)
826 for k,v in pairs(self.defaults) do
827 self.map:set(section, k, v)
831 self.map.proceed = true
837 SimpleSection = class(AbstractSection)
839 function SimpleSection.__init__(self, form, ...)
840 AbstractSection.__init__(self, form, nil, ...)
841 self.template = "cbi/nullsection"
845 Table = class(AbstractSection)
847 function Table.__init__(self, form, data, ...)
848 local datasource = {}
850 datasource.config = "table"
851 self.data = data or {}
853 datasource.formvalue = Map.formvalue
854 datasource.formvaluetable = Map.formvaluetable
855 datasource.readinput = true
857 function datasource.get(self, section, option)
858 return tself.data[section] and tself.data[section][option]
861 function datasource.submitstate(self)
862 return Map.formvalue(self, "cbi.submit")
865 function datasource.del(...)
869 function datasource.get_scheme()
873 AbstractSection.__init__(self, datasource, "table", ...)
874 self.template = "cbi/tblsection"
875 self.rowcolors = true
876 self.anonymous = true
879 function Table.parse(self, readinput)
880 self.map.readinput = (readinput ~= false)
881 for i, k in ipairs(self:cfgsections()) do
882 if self.map:submitstate() then
888 function Table.cfgsections(self)
891 for i, v in luci.util.kspairs(self.data) do
892 table.insert(sections, i)
898 function Table.update(self, data)
905 NamedSection - A fixed configuration section defined by its name
907 NamedSection = class(AbstractSection)
909 function NamedSection.__init__(self, map, section, stype, ...)
910 AbstractSection.__init__(self, map, stype, ...)
911 Node._i18n(self, map.config, section, nil, ...)
914 self.addremove = false
916 -- Use defaults from UVL
917 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
918 local vs = self.map:get_scheme(self.sectiontype)
919 self.addremove = not vs.unique and not vs.required
920 self.dynamic = vs.dynamic
921 self.title = self.title or vs.title
922 self.description = self.description or vs.descr
925 self.template = "cbi/nsection"
926 self.section = section
929 function NamedSection.parse(self, novld)
930 local s = self.section
931 local active = self:cfgvalue(s)
933 if self.addremove then
934 local path = self.config.."."..s
935 if active then -- Remove the section
936 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
940 else -- Create and apply default values
941 if self.map:formvalue("cbi.cns."..path) then
949 AbstractSection.parse_dynamic(self, s)
950 if self.map:submitstate() then
953 if not novld and not self.override_scheme and self.map.scheme then
954 _uvl_validate_section(self, s)
957 AbstractSection.parse_optionals(self, s)
967 TypedSection - A (set of) configuration section(s) defined by the type
968 addremove: Defines whether the user can add/remove sections of this type
969 anonymous: Allow creating anonymous sections
970 validate: a validation function returning nil if the section is invalid
972 TypedSection = class(AbstractSection)
974 function TypedSection.__init__(self, map, type, ...)
975 AbstractSection.__init__(self, map, type, ...)
976 Node._i18n(self, map.config, type, nil, ...)
978 self.template = "cbi/tsection"
980 self.anonymous = false
982 -- Use defaults from UVL
983 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
984 local vs = self.map:get_scheme(self.sectiontype)
985 self.addremove = not vs.unique and not vs.required
986 self.dynamic = vs.dynamic
987 self.anonymous = not vs.named
988 self.title = self.title or vs.title
989 self.description = self.description or vs.descr
993 -- Return all matching UCI sections for this TypedSection
994 function TypedSection.cfgsections(self)
996 self.map.uci:foreach(self.map.config, self.sectiontype,
998 if self:checkscope(section[".name"]) then
999 table.insert(sections, section[".name"])
1006 -- Limits scope to sections that have certain option => value pairs
1007 function TypedSection.depends(self, option, value)
1008 table.insert(self.deps, {option=option, value=value})
1011 function TypedSection.parse(self, novld)
1012 if self.addremove then
1014 local crval = REMOVE_PREFIX .. self.config
1015 local name = self.map:formvaluetable(crval)
1016 for k,v in pairs(name) do
1017 if k:sub(-2) == ".x" then
1018 k = k:sub(1, #k - 2)
1020 if self:cfgvalue(k) and self:checkscope(k) then
1027 for i, k in ipairs(self:cfgsections()) do
1028 AbstractSection.parse_dynamic(self, k)
1029 if self.map:submitstate() then
1030 Node.parse(self, k, novld)
1032 if not novld and not self.override_scheme and self.map.scheme then
1033 _uvl_validate_section(self, k)
1036 AbstractSection.parse_optionals(self, k)
1039 if self.addremove then
1042 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1043 local name = self.map:formvalue(crval)
1044 if self.anonymous then
1046 created = self:create()
1050 -- Ignore if it already exists
1051 if self:cfgvalue(name) then
1055 name = self:checkscope(name)
1058 self.err_invalid = true
1061 if name and #name > 0 then
1062 created = self:create(name) and name
1064 self.invalid_cts = true
1071 AbstractSection.parse_optionals(self, created)
1075 if created or self.changed then
1080 -- Verifies scope of sections
1081 function TypedSection.checkscope(self, section)
1082 -- Check if we are not excluded
1083 if self.filter and not self:filter(section) then
1087 -- Check if at least one dependency is met
1088 if #self.deps > 0 and self:cfgvalue(section) then
1091 for k, v in ipairs(self.deps) do
1092 if self:cfgvalue(section)[v.option] == v.value then
1102 return self:validate(section)
1106 -- Dummy validate function
1107 function TypedSection.validate(self, section)
1113 AbstractValue - An abstract Value Type
1114 null: Value can be empty
1115 valid: A function returning the value if it is valid otherwise nil
1116 depends: A table of option => value pairs of which one must be true
1117 default: The default value
1118 size: The size of the input fields
1119 rmempty: Unset value if empty
1120 optional: This value is optional (see AbstractSection.optionals)
1122 AbstractValue = class(Node)
1124 function AbstractValue.__init__(self, map, section, option, ...)
1125 Node.__init__(self, ...)
1126 self.section = section
1127 self.option = option
1129 self.config = map.config
1130 self.tag_invalid = {}
1131 self.tag_missing = {}
1132 self.tag_reqerror = {}
1135 --self.cast = "string"
1137 self.track_missing = false
1141 self.optional = false
1144 function AbstractValue.prepare(self)
1145 -- Use defaults from UVL
1146 if not self.override_scheme
1147 and self.map:get_scheme(self.section.sectiontype, self.option) then
1148 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1149 if self.cast == nil then
1150 self.cast = (vs.type == "list") and "list" or "string"
1152 self.title = self.title or vs.title
1153 self.description = self.description or vs.descr
1154 if self.default == nil then
1155 self.default = vs.default
1158 if vs.depends and not self.override_dependencies then
1159 for i, deps in ipairs(vs.depends) do
1160 deps = _uvl_strip_remote_dependencies(deps)
1168 self.cast = self.cast or "string"
1171 -- Add a dependencie to another section field
1172 function AbstractValue.depends(self, field, value)
1174 if type(field) == "string" then
1181 table.insert(self.deps, {deps=deps, add=""})
1184 -- Generates the unique CBID
1185 function AbstractValue.cbid(self, section)
1186 return "cbid."..self.map.config.."."..section.."."..self.option
1189 -- Return whether this object should be created
1190 function AbstractValue.formcreated(self, section)
1191 local key = "cbi.opt."..self.config.."."..section
1192 return (self.map:formvalue(key) == self.option)
1195 -- Returns the formvalue for this object
1196 function AbstractValue.formvalue(self, section)
1197 return self.map:formvalue(self:cbid(section))
1200 function AbstractValue.additional(self, value)
1201 self.optional = value
1204 function AbstractValue.mandatory(self, value)
1205 self.rmempty = not value
1208 function AbstractValue.parse(self, section, novld)
1209 local fvalue = self:formvalue(section)
1210 local cvalue = self:cfgvalue(section)
1212 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1213 fvalue = self:transform(self:validate(fvalue, section))
1214 if not fvalue and not novld then
1216 self.error[section] = "invalid"
1218 self.error = { [section] = "invalid" }
1220 self.map.save = false
1222 if fvalue and not (fvalue == cvalue) then
1223 if self:write(section, fvalue) then
1225 self.section.changed = true
1226 --luci.util.append(self.map.events, self.events)
1229 else -- Unset the UCI or error
1230 if self.rmempty or self.optional then
1231 if self:remove(section) then
1233 self.section.changed = true
1234 --luci.util.append(self.map.events, self.events)
1236 elseif cvalue ~= fvalue and not novld then
1237 self:write(section, fvalue or "")
1239 self.error[section] = "missing"
1241 self.error = { [section] = "missing" }
1243 self.map.save = false
1248 -- Render if this value exists or if it is mandatory
1249 function AbstractValue.render(self, s, scope)
1250 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1253 scope.cbid = self:cbid(s)
1254 scope.striptags = luci.util.striptags
1256 scope.ifattr = function(cond,key,val)
1258 return string.format(
1259 ' %s="%s"', tostring(key),
1260 luci.util.pcdata(tostring( val
1262 or (type(self[key]) ~= "function" and self[key])
1270 scope.attr = function(...)
1271 return scope.ifattr( true, ... )
1274 Node.render(self, scope)
1278 -- Return the UCI value of this object
1279 function AbstractValue.cfgvalue(self, section)
1280 local value = self.map:get(section, self.option)
1283 elseif not self.cast or self.cast == type(value) then
1285 elseif self.cast == "string" then
1286 if type(value) == "table" then
1289 elseif self.cast == "table" then
1290 return luci.util.split(value, "%s+", nil, true)
1294 -- Validate the form value
1295 function AbstractValue.validate(self, value)
1299 AbstractValue.transform = AbstractValue.validate
1303 function AbstractValue.write(self, section, value)
1304 return self.map:set(section, self.option, value)
1308 function AbstractValue.remove(self, section)
1309 return self.map:del(section, self.option)
1316 Value - A one-line value
1317 maxlength: The maximum length
1319 Value = class(AbstractValue)
1321 function Value.__init__(self, ...)
1322 AbstractValue.__init__(self, ...)
1323 self.template = "cbi/value"
1328 function Value.value(self, key, val)
1330 table.insert(self.keylist, tostring(key))
1331 table.insert(self.vallist, tostring(val))
1335 -- DummyValue - This does nothing except being there
1336 DummyValue = class(AbstractValue)
1338 function DummyValue.__init__(self, ...)
1339 AbstractValue.__init__(self, ...)
1340 self.template = "cbi/dvalue"
1344 function DummyValue.cfgvalue(self, section)
1347 if type(self.value) == "function" then
1348 value = self:value(section)
1353 value = AbstractValue.cfgvalue(self, section)
1358 function DummyValue.parse(self)
1364 Flag - A flag being enabled or disabled
1366 Flag = class(AbstractValue)
1368 function Flag.__init__(self, ...)
1369 AbstractValue.__init__(self, ...)
1370 self.template = "cbi/fvalue"
1376 -- A flag can only have two states: set or unset
1377 function Flag.parse(self, section)
1378 local fvalue = self:formvalue(section)
1381 fvalue = self.enabled
1383 fvalue = self.disabled
1386 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1387 if not(fvalue == self:cfgvalue(section)) then
1388 self:write(section, fvalue)
1391 self:remove(section)
1398 ListValue - A one-line value predefined in a list
1399 widget: The widget that will be used (select, radio)
1401 ListValue = class(AbstractValue)
1403 function ListValue.__init__(self, ...)
1404 AbstractValue.__init__(self, ...)
1405 self.template = "cbi/lvalue"
1410 self.widget = "select"
1413 function ListValue.prepare(self, ...)
1414 AbstractValue.prepare(self, ...)
1415 if not self.override_scheme
1416 and self.map:get_scheme(self.section.sectiontype, self.option) then
1417 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1418 if self.value and vs.valuelist and not self.override_values then
1419 for k, v in ipairs(vs.valuelist) do
1421 if not self.override_dependencies
1422 and vs.enum_depends and vs.enum_depends[v.value] then
1423 for i, dep in ipairs(vs.enum_depends[v.value]) do
1424 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1427 self:value(v.value, v.title or v.value, unpack(deps))
1433 function ListValue.value(self, key, val, ...)
1434 if luci.util.contains(self.keylist, key) then
1439 table.insert(self.keylist, tostring(key))
1440 table.insert(self.vallist, tostring(val))
1442 for i, deps in ipairs({...}) do
1443 table.insert(self.deps, {add = "-"..key, deps=deps})
1447 function ListValue.validate(self, val)
1448 if luci.util.contains(self.keylist, val) then
1458 MultiValue - Multiple delimited values
1459 widget: The widget that will be used (select, checkbox)
1460 delimiter: The delimiter that will separate the values (default: " ")
1462 MultiValue = class(AbstractValue)
1464 function MultiValue.__init__(self, ...)
1465 AbstractValue.__init__(self, ...)
1466 self.template = "cbi/mvalue"
1471 self.widget = "checkbox"
1472 self.delimiter = " "
1475 function MultiValue.render(self, ...)
1476 if self.widget == "select" and not self.size then
1477 self.size = #self.vallist
1480 AbstractValue.render(self, ...)
1483 function MultiValue.value(self, key, val)
1484 if luci.util.contains(self.keylist, key) then
1489 table.insert(self.keylist, tostring(key))
1490 table.insert(self.vallist, tostring(val))
1493 function MultiValue.valuelist(self, section)
1494 local val = self:cfgvalue(section)
1496 if not(type(val) == "string") then
1500 return luci.util.split(val, self.delimiter)
1503 function MultiValue.validate(self, val)
1504 val = (type(val) == "table") and val or {val}
1508 for i, value in ipairs(val) do
1509 if luci.util.contains(self.keylist, value) then
1510 result = result and (result .. self.delimiter .. value) or value
1518 StaticList = class(MultiValue)
1520 function StaticList.__init__(self, ...)
1521 MultiValue.__init__(self, ...)
1523 self.valuelist = self.cfgvalue
1525 if not self.override_scheme
1526 and self.map:get_scheme(self.section.sectiontype, self.option) then
1527 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1528 if self.value and vs.values and not self.override_values then
1529 for k, v in pairs(vs.values) do
1536 function StaticList.validate(self, value)
1537 value = (type(value) == "table") and value or {value}
1540 for i, v in ipairs(value) do
1541 if luci.util.contains(self.keylist, v) then
1542 table.insert(valid, v)
1549 DynamicList = class(AbstractValue)
1551 function DynamicList.__init__(self, ...)
1552 AbstractValue.__init__(self, ...)
1553 self.template = "cbi/dynlist"
1559 function DynamicList.value(self, key, val)
1561 table.insert(self.keylist, tostring(key))
1562 table.insert(self.vallist, tostring(val))
1565 function DynamicList.write(self, ...)
1566 self.map.proceed = true
1567 return AbstractValue.write(self, ...)
1570 function DynamicList.formvalue(self, section)
1571 local value = AbstractValue.formvalue(self, section)
1572 value = (type(value) == "table") and value or {value}
1575 for i, v in ipairs(value) do
1577 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1578 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1579 table.insert(valid, v)
1588 TextValue - A multi-line value
1591 TextValue = class(AbstractValue)
1593 function TextValue.__init__(self, ...)
1594 AbstractValue.__init__(self, ...)
1595 self.template = "cbi/tvalue"
1601 Button = class(AbstractValue)
1603 function Button.__init__(self, ...)
1604 AbstractValue.__init__(self, ...)
1605 self.template = "cbi/button"
1606 self.inputstyle = nil
1611 FileUpload = class(AbstractValue)
1613 function FileUpload.__init__(self, ...)
1614 AbstractValue.__init__(self, ...)
1615 self.template = "cbi/upload"
1616 if not self.map.upload_fields then
1617 self.map.upload_fields = { self }
1619 self.map.upload_fields[#self.map.upload_fields+1] = self
1623 function FileUpload.formcreated(self, section)
1624 return AbstractValue.formcreated(self, section) or
1625 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1626 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1629 function FileUpload.cfgvalue(self, section)
1630 local val = AbstractValue.cfgvalue(self, section)
1631 if val and luci.fs.access(val) then
1637 function FileUpload.formvalue(self, section)
1638 local val = AbstractValue.formvalue(self, section)
1640 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1641 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1651 function FileUpload.remove(self, section)
1652 local val = AbstractValue.formvalue(self, section)
1653 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1654 return AbstractValue.remove(self, section)
1658 FileBrowser = class(AbstractValue)
1660 function FileBrowser.__init__(self, ...)
1661 AbstractValue.__init__(self, ...)
1662 self.template = "cbi/browser"