2 LuCI - Configuration Bind Interface
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
12 Copyright 2008 Steven Barth <steven@midlink.org>
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at
18 http://www.apache.org/licenses/LICENSE-2.0
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
27 module("luci.cbi", package.seeall)
29 require("luci.template")
30 local util = require("luci.util")
35 --local event = require "luci.sys.event"
36 local uci = require("luci.model.uci")
37 local class = util.class
38 local instanceof = util.instanceof
48 CREATE_PREFIX = "cbi.cts."
49 REMOVE_PREFIX = "cbi.rts."
51 -- Loads a CBI map from given file, creating an environment and returns it
52 function load(cbimap, ...)
54 local i18n = require "luci.i18n"
55 require("luci.config")
58 local upldir = "/lib/uci/upload/"
59 local cbidir = luci.util.libpath() .. "/model/cbi/"
61 assert(luci.fs.stat(cbimap) or luci.fs.stat(cbidir..cbimap..".lua"),
64 local func, err = loadfile(cbimap)
66 func, err = loadfile(cbidir..cbimap..".lua")
70 luci.i18n.loadc("cbi")
71 luci.i18n.loadc("uvl")
74 translate=i18n.translate,
75 translatef=i18n.translatef,
79 setfenv(func, setmetatable(env, {__index =
81 return rawget(tbl, key) or _M[key] or _G[key]
84 local maps = { func() }
86 local has_upload = false
88 for i, map in ipairs(maps) do
89 if not instanceof(map, Node) then
90 error("CBI map returns no valid map object!")
94 if map.upload_fields then
96 for _, field in ipairs(map.upload_fields) do
98 field.config .. '.' ..
99 field.section.sectiontype .. '.' ..
108 local uci = luci.model.uci.cursor()
109 local prm = luci.http.context.request.message.params
112 luci.http.setfilehandler(
113 function( field, chunk, eof )
114 if not field then return end
115 if field.name and not cbid then
116 local c, s, o = field.name:gmatch(
117 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
120 if c and s and o then
121 local t = uci:get( c, s )
122 if t and uploads[c.."."..t.."."..o] then
123 local path = upldir .. field.name
124 fd = io.open(path, "w")
133 if field.name == cbid and fd then
149 local function _uvl_validate_section(node, name)
150 local co = node.map:get()
152 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
153 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
155 local function tag_fields(e)
156 if e.option and node.fields[e.option] then
157 if node.fields[e.option].error then
158 node.fields[e.option].error[name] = e
160 node.fields[e.option].error = { [name] = e }
163 for _, c in ipairs(e.childs) do tag_fields(c) end
167 local function tag_section(e)
169 for _, c in ipairs(e.childs) do
170 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
171 table.insert( s, c.childs[1]:string() )
173 table.insert( s, c:string() )
180 node.error = { [name] = s }
185 local stat, err = node.map.validator:validate_section(node.config, name, co)
187 node.map.save = false
194 local function _uvl_strip_remote_dependencies(deps)
197 for k, v in pairs(deps) do
198 k = k:gsub("%$config%.%$section%.", "")
199 if k:match("^[%w_]+$") and type(v) == "string" then
208 -- Node pseudo abstract class
211 function Node.__init__(self, title, description)
213 self.title = title or ""
214 self.description = description or ""
215 self.template = "cbi/node"
219 function Node._i18n(self, config, section, option, title, description)
222 if type(luci.i18n) == "table" then
224 local key = config and config:gsub("[^%w]+", "") or ""
226 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
227 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
229 self.title = title or luci.i18n.translate( key, option or section or config )
230 self.description = description or luci.i18n.translate( key .. "_desc", "" )
235 function Node.prepare(self, ...)
236 for k, child in ipairs(self.children) do
241 -- Append child nodes
242 function Node.append(self, obj)
243 table.insert(self.children, obj)
246 -- Parse this node and its children
247 function Node.parse(self, ...)
248 for k, child in ipairs(self.children) do
254 function Node.render(self, scope)
258 luci.template.render(self.template, scope)
261 -- Render the children
262 function Node.render_children(self, ...)
263 for k, node in ipairs(self.children) do
270 A simple template element
272 Template = class(Node)
274 function Template.__init__(self, template)
276 self.template = template
279 function Template.render(self)
280 luci.template.render(self.template, {self=self})
285 Map - A map describing a configuration file
289 function Map.__init__(self, config, ...)
290 Node.__init__(self, ...)
291 Node._i18n(self, config, nil, nil, ...)
294 self.parsechain = {self.config}
295 self.template = "cbi/map"
296 self.apply_on_parse = nil
297 self.readinput = true
299 self.uci = uci.cursor()
304 if not self.uci:load(self.config) then
305 error("Unable to read UCI data: " .. self.config)
308 self.validator = luci.uvl.UVL()
309 self.scheme = self.validator:get_scheme(self.config)
313 function Map.formvalue(self, key)
314 return self.readinput and luci.http.formvalue(key)
317 function Map.formvaluetable(self, key)
318 return self.readinput and luci.http.formvaluetable(key)
321 function Map.get_scheme(self, sectiontype, option)
323 return self.scheme and self.scheme.sections[sectiontype]
325 return self.scheme and self.scheme.variables[sectiontype]
326 and self.scheme.variables[sectiontype][option]
330 function Map.submitstate(self)
331 return self:formvalue("cbi.submit")
334 -- Chain foreign config
335 function Map.chain(self, config)
336 table.insert(self.parsechain, config)
339 function Map.state_handler(self, state)
343 -- Use optimized UCI writing
344 function Map.parse(self, readinput, ...)
345 self.readinput = (readinput ~= false)
346 Node.parse(self, ...)
349 for i, config in ipairs(self.parsechain) do
350 self.uci:save(config)
352 if self:submitstate() and (self.autoapply or luci.http.formvalue("cbi.apply")) then
353 for i, config in ipairs(self.parsechain) do
354 self.uci:commit(config)
356 -- Refresh data because commit changes section names
357 self.uci:load(config)
359 if self.apply_on_parse then
360 self.uci:apply(self.parsechain)
362 self._apply = function()
363 local cmd = self.uci:apply(self.parsechain, true)
369 Node.parse(self, true)
372 for i, config in ipairs(self.parsechain) do
373 self.uci:unload(config)
375 if type(self.commit_handler) == "function" then
376 self:commit_handler(self:submitstate())
380 if self:submitstate() then
382 self.state = self.changed and FORM_CHANGED or FORM_VALID
384 self.state = FORM_INVALID
387 self.state = FORM_NODATA
390 return self:state_handler(self.state)
393 function Map.render(self, ...)
394 Node.render(self, ...)
396 local fp = self._apply()
402 -- Creates a child section
403 function Map.section(self, class, ...)
404 if instanceof(class, AbstractSection) then
405 local obj = class(self, ...)
409 error("class must be a descendent of AbstractSection")
414 function Map.add(self, sectiontype)
415 return self.uci:add(self.config, sectiontype)
419 function Map.set(self, section, option, value)
421 return self.uci:set(self.config, section, option, value)
423 return self.uci:set(self.config, section, value)
428 function Map.del(self, section, option)
430 return self.uci:delete(self.config, section, option)
432 return self.uci:delete(self.config, section)
437 function Map.get(self, section, option)
439 return self.uci:get_all(self.config)
441 return self.uci:get(self.config, section, option)
443 return self.uci:get_all(self.config, section)
450 Compound = class(Node)
452 function Compound.__init__(self, ...)
454 self.children = {...}
457 function Compound.parse(self, ...)
458 local cstate, state = 0, 0
460 for k, child in ipairs(self.children) do
461 cstate = child:parse(...)
462 state = (not state or cstate < state) and cstate or state
470 Delegator - Node controller
472 Delegator = class(Node)
473 function Delegator.__init__(self, ...)
474 Node.__init__(self, ...)
476 self.template = "cbi/delegator"
479 function Delegator.state(self, name, node, transitor)
480 transitor = transitor or self.transistor_linear
481 local state = {node=node, name=name, transitor=transitor}
483 assert(instanceof(node, Node), "Invalid node")
484 assert(not self.nodes[name], "Duplicate entry")
486 self.nodes[name] = state
492 function Delegator.get(self, name)
493 return self.nodes[name]
496 function Delegator.transistor_linear(self, state, cstate)
498 for i, child in ipairs(self.children) do
499 if state == child then
500 return self.children[i+1]
508 function Delegator.parse(self, ...)
509 local active = self:getactive()
510 assert(active, "Invalid state")
512 local cstate = active.node:parse()
513 self.active = active.transistor(self, active.node, cstate)
515 if not self.active then
518 self.active:parse(false)
523 function Delegator.render(self, ...)
524 self.active.node:render(...)
527 function Delegator.getactive(self)
528 return self:get(Map.formvalue(self, "cbi.delegated")
529 or (self.children[1] and self.children[1].name))
537 Page.__init__ = Node.__init__
538 Page.parse = function() end
542 SimpleForm - A Simple non-UCI form
544 SimpleForm = class(Node)
546 function SimpleForm.__init__(self, config, title, description, data)
547 Node.__init__(self, title, description)
549 self.data = data or {}
550 self.template = "cbi/simpleform"
552 self.pageaction = false
553 self.readinput = true
556 SimpleForm.formvalue = Map.formvalue
557 SimpleForm.formvaluetable = Map.formvaluetable
559 function SimpleForm.parse(self, readinput, ...)
560 self.readinput = (readinput ~= false)
561 if self:submitstate() then
562 Node.parse(self, 1, ...)
566 for k, j in ipairs(self.children) do
567 for i, v in ipairs(j.children) do
569 and (not v.tag_missing or not v.tag_missing[1])
570 and (not v.tag_invalid or not v.tag_invalid[1])
576 not self:submitstate() and FORM_NODATA
577 or valid and FORM_VALID
580 self.dorender = not self.handle or self:handle(state, self.data) ~= false
584 function SimpleForm.render(self, ...)
585 if self.dorender then
586 Node.render(self, ...)
590 function SimpleForm.submitstate(self)
591 return self:formvalue("cbi.submit")
594 function SimpleForm.section(self, class, ...)
595 if instanceof(class, AbstractSection) then
596 local obj = class(self, ...)
600 error("class must be a descendent of AbstractSection")
604 -- Creates a child field
605 function SimpleForm.field(self, class, ...)
607 for k, v in ipairs(self.children) do
608 if instanceof(v, SimpleSection) then
614 section = self:section(SimpleSection)
617 if instanceof(class, AbstractValue) then
618 local obj = class(self, section, ...)
619 obj.track_missing = true
623 error("class must be a descendent of AbstractValue")
627 function SimpleForm.set(self, section, option, value)
628 self.data[option] = value
632 function SimpleForm.del(self, section, option)
633 self.data[option] = nil
637 function SimpleForm.get(self, section, option)
638 return self.data[option]
642 function SimpleForm.get_scheme()
651 AbstractSection = class(Node)
653 function AbstractSection.__init__(self, map, sectiontype, ...)
654 Node.__init__(self, ...)
655 self.sectiontype = sectiontype
657 self.config = map.config
662 self.tag_invalid = {}
663 self.tag_deperror = {}
667 self.addremove = false
671 -- Appends a new option
672 function AbstractSection.option(self, class, option, ...)
673 -- Autodetect from UVL
674 if class == true and self.map:get_scheme(self.sectiontype, option) then
675 local vs = self.map:get_scheme(self.sectiontype, option)
676 if vs.type == "boolean" then
678 elseif vs.type == "list" then
680 elseif vs.type == "enum" or vs.type == "reference" then
687 if instanceof(class, AbstractValue) then
688 local obj = class(self.map, self, option, ...)
690 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
693 self.fields[option] = obj
695 elseif class == true then
696 error("No valid class was given and autodetection failed.")
698 error("class must be a descendant of AbstractValue")
702 -- Parse optional options
703 function AbstractSection.parse_optionals(self, section)
704 if not self.optional then
708 self.optionals[section] = {}
710 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
711 for k,v in ipairs(self.children) do
712 if v.optional and not v:cfgvalue(section) then
713 if field == v.option then
716 table.insert(self.optionals[section], v)
721 if field and #field > 0 and self.dynamic then
722 self:add_dynamic(field)
726 -- Add a dynamic option
727 function AbstractSection.add_dynamic(self, field, optional)
728 local o = self:option(Value, field, field)
729 o.optional = optional
732 -- Parse all dynamic options
733 function AbstractSection.parse_dynamic(self, section)
734 if not self.dynamic then
738 local arr = luci.util.clone(self:cfgvalue(section))
739 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
740 for k, v in pairs(form) do
744 for key,val in pairs(arr) do
747 for i,c in ipairs(self.children) do
748 if c.option == key then
753 if create and key:sub(1, 1) ~= "." then
754 self:add_dynamic(key, true)
759 -- Returns the section's UCI table
760 function AbstractSection.cfgvalue(self, section)
761 return self.map:get(section)
765 function AbstractSection.push_events(self)
766 --luci.util.append(self.map.events, self.events)
767 self.map.changed = true
770 -- Removes the section
771 function AbstractSection.remove(self, section)
772 self.map.autoapply = false
773 return self.map:del(section)
776 -- Creates the section
777 function AbstractSection.create(self, section)
781 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
783 section = self.map:add(self.sectiontype)
788 for k,v in pairs(self.children) do
790 self.map:set(section, v.option, v.default)
794 for k,v in pairs(self.defaults) do
795 self.map:set(section, k, v)
799 self.map.autoapply = false
805 SimpleSection = class(AbstractSection)
807 function SimpleSection.__init__(self, form, ...)
808 AbstractSection.__init__(self, form, nil, ...)
809 self.template = "cbi/nullsection"
813 Table = class(AbstractSection)
815 function Table.__init__(self, form, data, ...)
816 local datasource = {}
817 datasource.config = "table"
820 datasource.formvalue = Map.formvalue
821 datasource.formvaluetable = Map.formvaluetable
822 datasource.readinput = true
824 function datasource.get(self, section, option)
825 return data[section] and data[section][option]
828 function datasource.submitstate(self)
829 return Map.formvalue(self, "cbi.submit")
832 function datasource.del(...)
836 function datasource.get_scheme()
840 AbstractSection.__init__(self, datasource, "table", ...)
841 self.template = "cbi/tblsection"
842 self.rowcolors = true
843 self.anonymous = true
846 function Table.parse(self, readinput)
847 self.map.readinput = (readinput ~= false)
848 for i, k in ipairs(self:cfgsections()) do
849 if self.map:submitstate() then
855 function Table.cfgsections(self)
858 for i, v in luci.util.kspairs(self.data) do
859 table.insert(sections, i)
868 NamedSection - A fixed configuration section defined by its name
870 NamedSection = class(AbstractSection)
872 function NamedSection.__init__(self, map, section, stype, ...)
873 AbstractSection.__init__(self, map, stype, ...)
874 Node._i18n(self, map.config, section, nil, ...)
877 self.addremove = false
879 -- Use defaults from UVL
880 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
881 local vs = self.map:get_scheme(self.sectiontype)
882 self.addremove = not vs.unique and not vs.required
883 self.dynamic = vs.dynamic
884 self.title = self.title or vs.title
885 self.description = self.description or vs.descr
888 self.template = "cbi/nsection"
889 self.section = section
892 function NamedSection.parse(self, novld)
893 local s = self.section
894 local active = self:cfgvalue(s)
896 if self.addremove then
897 local path = self.config.."."..s
898 if active then -- Remove the section
899 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
903 else -- Create and apply default values
904 if self.map:formvalue("cbi.cns."..path) then
912 AbstractSection.parse_dynamic(self, s)
913 if self.map:submitstate() then
916 if not novld and not self.override_scheme and self.map.scheme then
917 _uvl_validate_section(self, s)
920 AbstractSection.parse_optionals(self, s)
930 TypedSection - A (set of) configuration section(s) defined by the type
931 addremove: Defines whether the user can add/remove sections of this type
932 anonymous: Allow creating anonymous sections
933 validate: a validation function returning nil if the section is invalid
935 TypedSection = class(AbstractSection)
937 function TypedSection.__init__(self, map, type, ...)
938 AbstractSection.__init__(self, map, type, ...)
939 Node._i18n(self, map.config, type, nil, ...)
941 self.template = "cbi/tsection"
943 self.anonymous = false
945 -- Use defaults from UVL
946 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
947 local vs = self.map:get_scheme(self.sectiontype)
948 self.addremove = not vs.unique and not vs.required
949 self.dynamic = vs.dynamic
950 self.anonymous = not vs.named
951 self.title = self.title or vs.title
952 self.description = self.description or vs.descr
956 -- Return all matching UCI sections for this TypedSection
957 function TypedSection.cfgsections(self)
959 self.map.uci:foreach(self.map.config, self.sectiontype,
961 if self:checkscope(section[".name"]) then
962 table.insert(sections, section[".name"])
969 -- Limits scope to sections that have certain option => value pairs
970 function TypedSection.depends(self, option, value)
971 table.insert(self.deps, {option=option, value=value})
974 function TypedSection.parse(self, novld)
975 if self.addremove then
977 local crval = REMOVE_PREFIX .. self.config
978 local name = self.map:formvaluetable(crval)
979 for k,v in pairs(name) do
980 if k:sub(-2) == ".x" then
983 if self:cfgvalue(k) and self:checkscope(k) then
990 for i, k in ipairs(self:cfgsections()) do
991 AbstractSection.parse_dynamic(self, k)
992 if self.map:submitstate() then
993 Node.parse(self, k, novld)
995 if not novld and not self.override_scheme and self.map.scheme then
996 _uvl_validate_section(self, k)
999 AbstractSection.parse_optionals(self, k)
1002 if self.addremove then
1005 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1006 local name = self.map:formvalue(crval)
1007 if self.anonymous then
1009 created = self:create()
1013 -- Ignore if it already exists
1014 if self:cfgvalue(name) then
1018 name = self:checkscope(name)
1021 self.err_invalid = true
1024 if name and #name > 0 then
1025 created = self:create(name) and name
1027 self.invalid_cts = true
1034 AbstractSection.parse_optionals(self, created)
1038 if created or self.changed then
1043 -- Verifies scope of sections
1044 function TypedSection.checkscope(self, section)
1045 -- Check if we are not excluded
1046 if self.filter and not self:filter(section) then
1050 -- Check if at least one dependency is met
1051 if #self.deps > 0 and self:cfgvalue(section) then
1054 for k, v in ipairs(self.deps) do
1055 if self:cfgvalue(section)[v.option] == v.value then
1065 return self:validate(section)
1069 -- Dummy validate function
1070 function TypedSection.validate(self, section)
1076 AbstractValue - An abstract Value Type
1077 null: Value can be empty
1078 valid: A function returning the value if it is valid otherwise nil
1079 depends: A table of option => value pairs of which one must be true
1080 default: The default value
1081 size: The size of the input fields
1082 rmempty: Unset value if empty
1083 optional: This value is optional (see AbstractSection.optionals)
1085 AbstractValue = class(Node)
1087 function AbstractValue.__init__(self, map, section, option, ...)
1088 Node.__init__(self, ...)
1089 self.section = section
1090 self.option = option
1092 self.config = map.config
1093 self.tag_invalid = {}
1094 self.tag_missing = {}
1095 self.tag_reqerror = {}
1098 --self.cast = "string"
1100 self.track_missing = false
1104 self.optional = false
1107 function AbstractValue.prepare(self)
1108 -- Use defaults from UVL
1109 if not self.override_scheme
1110 and self.map:get_scheme(self.section.sectiontype, self.option) then
1111 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1112 if self.cast == nil then
1113 self.cast = (vs.type == "list") and "list" or "string"
1115 self.title = self.title or vs.title
1116 self.description = self.description or vs.descr
1117 if self.default == nil then
1118 self.default = vs.default
1121 if vs.depends and not self.override_dependencies then
1122 for i, deps in ipairs(vs.depends) do
1123 deps = _uvl_strip_remote_dependencies(deps)
1131 self.cast = self.cast or "string"
1134 -- Add a dependencie to another section field
1135 function AbstractValue.depends(self, field, value)
1137 if type(field) == "string" then
1144 table.insert(self.deps, {deps=deps, add=""})
1147 -- Generates the unique CBID
1148 function AbstractValue.cbid(self, section)
1149 return "cbid."..self.map.config.."."..section.."."..self.option
1152 -- Return whether this object should be created
1153 function AbstractValue.formcreated(self, section)
1154 local key = "cbi.opt."..self.config.."."..section
1155 return (self.map:formvalue(key) == self.option)
1158 -- Returns the formvalue for this object
1159 function AbstractValue.formvalue(self, section)
1160 return self.map:formvalue(self:cbid(section))
1163 function AbstractValue.additional(self, value)
1164 self.optional = value
1167 function AbstractValue.mandatory(self, value)
1168 self.rmempty = not value
1171 function AbstractValue.parse(self, section, novld)
1172 local fvalue = self:formvalue(section)
1173 local cvalue = self:cfgvalue(section)
1175 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1176 fvalue = self:transform(self:validate(fvalue, section))
1177 if not fvalue and not novld then
1179 self.error[section] = "invalid"
1181 self.error = { [section] = "invalid" }
1183 self.map.save = false
1185 if fvalue and not (fvalue == cvalue) then
1186 if self:write(section, fvalue) then
1188 self.section.changed = true
1189 --luci.util.append(self.map.events, self.events)
1192 else -- Unset the UCI or error
1193 if self.rmempty or self.optional then
1194 if self:remove(section) then
1196 self.section.changed = true
1197 --luci.util.append(self.map.events, self.events)
1199 elseif cvalue ~= fvalue and not novld then
1200 self:write(section, fvalue or "")
1202 self.error[section] = "missing"
1204 self.error = { [section] = "missing" }
1206 self.map.save = false
1211 -- Render if this value exists or if it is mandatory
1212 function AbstractValue.render(self, s, scope)
1213 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1216 scope.cbid = self:cbid(s)
1217 scope.striptags = luci.util.striptags
1219 scope.ifattr = function(cond,key,val)
1221 return string.format(
1222 ' %s="%s"', tostring(key),
1223 luci.util.pcdata(tostring( val
1225 or (type(self[key]) ~= "function" and self[key])
1233 scope.attr = function(...)
1234 return scope.ifattr( true, ... )
1237 Node.render(self, scope)
1241 -- Return the UCI value of this object
1242 function AbstractValue.cfgvalue(self, section)
1243 local value = self.map:get(section, self.option)
1246 elseif not self.cast or self.cast == type(value) then
1248 elseif self.cast == "string" then
1249 if type(value) == "table" then
1252 elseif self.cast == "table" then
1253 return luci.util.split(value, "%s+", nil, true)
1257 -- Validate the form value
1258 function AbstractValue.validate(self, value)
1262 AbstractValue.transform = AbstractValue.validate
1266 function AbstractValue.write(self, section, value)
1267 -- Work around a bug in libuci-lua;
1268 -- list values are not overwritten but appended, resolve this
1269 -- by removing the value before
1270 if type(value) == "table" then
1271 self.map:del(section, self.option)
1274 return self.map:set(section, self.option, value)
1278 function AbstractValue.remove(self, section)
1279 return self.map:del(section, self.option)
1286 Value - A one-line value
1287 maxlength: The maximum length
1289 Value = class(AbstractValue)
1291 function Value.__init__(self, ...)
1292 AbstractValue.__init__(self, ...)
1293 self.template = "cbi/value"
1298 function Value.value(self, key, val)
1300 table.insert(self.keylist, tostring(key))
1301 table.insert(self.vallist, tostring(val))
1305 -- DummyValue - This does nothing except being there
1306 DummyValue = class(AbstractValue)
1308 function DummyValue.__init__(self, ...)
1309 AbstractValue.__init__(self, ...)
1310 self.template = "cbi/dvalue"
1314 function DummyValue.cfgvalue(self, section)
1317 if type(self.value) == "function" then
1318 value = self:value(section)
1323 value = AbstractValue.cfgvalue(self, section)
1328 function DummyValue.parse(self)
1334 Flag - A flag being enabled or disabled
1336 Flag = class(AbstractValue)
1338 function Flag.__init__(self, ...)
1339 AbstractValue.__init__(self, ...)
1340 self.template = "cbi/fvalue"
1346 -- A flag can only have two states: set or unset
1347 function Flag.parse(self, section)
1348 local fvalue = self:formvalue(section)
1351 fvalue = self.enabled
1353 fvalue = self.disabled
1356 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1357 if not(fvalue == self:cfgvalue(section)) then
1358 self:write(section, fvalue)
1361 self:remove(section)
1368 ListValue - A one-line value predefined in a list
1369 widget: The widget that will be used (select, radio)
1371 ListValue = class(AbstractValue)
1373 function ListValue.__init__(self, ...)
1374 AbstractValue.__init__(self, ...)
1375 self.template = "cbi/lvalue"
1380 self.widget = "select"
1383 function ListValue.prepare(self, ...)
1384 AbstractValue.prepare(self, ...)
1385 if not self.override_scheme
1386 and self.map:get_scheme(self.section.sectiontype, self.option) then
1387 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1388 if self.value and vs.valuelist and not self.override_values then
1389 for k, v in ipairs(vs.valuelist) do
1391 if not self.override_dependencies
1392 and vs.enum_depends and vs.enum_depends[v.value] then
1393 for i, dep in ipairs(vs.enum_depends[v.value]) do
1394 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1397 self:value(v.value, v.title or v.value, unpack(deps))
1403 function ListValue.value(self, key, val, ...)
1404 if luci.util.contains(self.keylist, key) then
1409 table.insert(self.keylist, tostring(key))
1410 table.insert(self.vallist, tostring(val))
1412 for i, deps in ipairs({...}) do
1413 table.insert(self.deps, {add = "-"..key, deps=deps})
1417 function ListValue.validate(self, val)
1418 if luci.util.contains(self.keylist, val) then
1428 MultiValue - Multiple delimited values
1429 widget: The widget that will be used (select, checkbox)
1430 delimiter: The delimiter that will separate the values (default: " ")
1432 MultiValue = class(AbstractValue)
1434 function MultiValue.__init__(self, ...)
1435 AbstractValue.__init__(self, ...)
1436 self.template = "cbi/mvalue"
1441 self.widget = "checkbox"
1442 self.delimiter = " "
1445 function MultiValue.render(self, ...)
1446 if self.widget == "select" and not self.size then
1447 self.size = #self.vallist
1450 AbstractValue.render(self, ...)
1453 function MultiValue.value(self, key, val)
1454 if luci.util.contains(self.keylist, key) then
1459 table.insert(self.keylist, tostring(key))
1460 table.insert(self.vallist, tostring(val))
1463 function MultiValue.valuelist(self, section)
1464 local val = self:cfgvalue(section)
1466 if not(type(val) == "string") then
1470 return luci.util.split(val, self.delimiter)
1473 function MultiValue.validate(self, val)
1474 val = (type(val) == "table") and val or {val}
1478 for i, value in ipairs(val) do
1479 if luci.util.contains(self.keylist, value) then
1480 result = result and (result .. self.delimiter .. value) or value
1488 StaticList = class(MultiValue)
1490 function StaticList.__init__(self, ...)
1491 MultiValue.__init__(self, ...)
1493 self.valuelist = self.cfgvalue
1495 if not self.override_scheme
1496 and self.map:get_scheme(self.section.sectiontype, self.option) then
1497 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1498 if self.value and vs.values and not self.override_values then
1499 for k, v in pairs(vs.values) do
1506 function StaticList.validate(self, value)
1507 value = (type(value) == "table") and value or {value}
1510 for i, v in ipairs(value) do
1511 if luci.util.contains(self.keylist, v) then
1512 table.insert(valid, v)
1519 DynamicList = class(AbstractValue)
1521 function DynamicList.__init__(self, ...)
1522 AbstractValue.__init__(self, ...)
1523 self.template = "cbi/dynlist"
1529 function DynamicList.value(self, key, val)
1531 table.insert(self.keylist, tostring(key))
1532 table.insert(self.vallist, tostring(val))
1535 function DynamicList.formvalue(self, section)
1536 local value = AbstractValue.formvalue(self, section)
1537 value = (type(value) == "table") and value or {value}
1540 for i, v in ipairs(value) do
1542 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1543 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1544 table.insert(valid, v)
1553 TextValue - A multi-line value
1556 TextValue = class(AbstractValue)
1558 function TextValue.__init__(self, ...)
1559 AbstractValue.__init__(self, ...)
1560 self.template = "cbi/tvalue"
1566 Button = class(AbstractValue)
1568 function Button.__init__(self, ...)
1569 AbstractValue.__init__(self, ...)
1570 self.template = "cbi/button"
1571 self.inputstyle = nil
1576 FileUpload = class(AbstractValue)
1578 function FileUpload.__init__(self, ...)
1579 AbstractValue.__init__(self, ...)
1580 self.template = "cbi/upload"
1581 if not self.map.upload_fields then
1582 self.map.upload_fields = { self }
1584 self.map.upload_fields[#self.map.upload_fields+1] = self
1588 function FileUpload.formcreated(self, section)
1589 return AbstractValue.formcreated(self, section) or
1590 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1591 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1594 function FileUpload.cfgvalue(self, section)
1595 local val = AbstractValue.cfgvalue(self, section)
1596 if val and luci.fs.access(val) then
1602 function FileUpload.formvalue(self, section)
1603 local val = AbstractValue.formvalue(self, section)
1605 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1606 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1616 function FileUpload.remove(self, section)
1617 local val = AbstractValue.formvalue(self, section)
1618 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1619 return AbstractValue.remove(self, section)
1623 FileBrowser = class(AbstractValue)
1625 function FileBrowser.__init__(self, ...)
1626 AbstractValue.__init__(self, ...)
1627 self.template = "cbi/browser"