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")
35 local uci = require("luci.model.uci")
36 local class = luci.util.class
37 local instanceof = luci.util.instanceof
46 CREATE_PREFIX = "cbi.cts."
47 REMOVE_PREFIX = "cbi.rts."
49 -- Loads a CBI map from given file, creating an environment and returns it
50 function load(cbimap, ...)
52 local i18n = require "luci.i18n"
53 require("luci.config")
56 local upldir = "/lib/uci/upload/"
57 local cbidir = luci.util.libpath() .. "/model/cbi/"
58 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
61 luci.i18n.loadc("cbi")
62 luci.i18n.loadc("uvl")
65 translate=i18n.translate,
66 translatef=i18n.translatef,
70 setfenv(func, setmetatable(env, {__index =
72 return rawget(tbl, key) or _M[key] or _G[key]
75 local maps = { func() }
77 local has_upload = false
79 for i, map in ipairs(maps) do
80 if not instanceof(map, Node) then
81 error("CBI map returns no valid map object!")
85 if map.upload_fields then
87 for _, field in ipairs(map.upload_fields) do
89 field.config .. '.' ..
90 field.section.sectiontype .. '.' ..
99 local uci = luci.model.uci.cursor()
100 local prm = luci.http.context.request.message.params
103 luci.http.setfilehandler(
104 function( field, chunk, eof )
105 if not field then return end
106 if field.name and not cbid then
107 local c, s, o = field.name:gmatch(
108 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
111 if c and s and o then
112 local t = uci:get( c, s )
113 if t and uploads[c.."."..t.."."..o] then
114 local path = upldir .. field.name
115 fd = io.open(path, "w")
124 if field.name == cbid and fd then
140 local function _uvl_validate_section(node, name)
141 local co = node.map:get()
143 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
144 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
146 local function tag_fields(e)
147 if e.option and node.fields[e.option] then
148 if node.fields[e.option].error then
149 node.fields[e.option].error[name] = e
151 node.fields[e.option].error = { [name] = e }
154 for _, c in ipairs(e.childs) do tag_fields(c) end
158 local function tag_section(e)
160 for _, c in ipairs(e.childs) do
161 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
162 table.insert( s, c.childs[1]:string() )
164 table.insert( s, c:string() )
171 node.error = { [name] = s }
176 local stat, err = node.map.validator:validate_section(node.config, name, co)
178 node.map.save = false
185 local function _uvl_strip_remote_dependencies(deps)
188 for k, v in pairs(deps) do
189 k = k:gsub("%$config%.%$section%.", "")
190 if k:match("^[%w_]+$") and type(v) == "string" then
199 -- Node pseudo abstract class
202 function Node.__init__(self, title, description)
204 self.title = title or ""
205 self.description = description or ""
206 self.template = "cbi/node"
210 function Node._i18n(self, config, section, option, title, description)
213 if type(luci.i18n) == "table" then
215 local key = config and config:gsub("[^%w]+", "") or ""
217 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
218 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
220 self.title = title or luci.i18n.translate( key, option or section or config )
221 self.description = description or luci.i18n.translate( key .. "_desc", "" )
226 function Node.prepare(self, ...)
227 for k, child in ipairs(self.children) do
232 -- Append child nodes
233 function Node.append(self, obj)
234 table.insert(self.children, obj)
237 -- Parse this node and its children
238 function Node.parse(self, ...)
239 for k, child in ipairs(self.children) do
245 function Node.render(self, scope)
249 luci.template.render(self.template, scope)
252 -- Render the children
253 function Node.render_children(self, ...)
254 for k, node in ipairs(self.children) do
261 A simple template element
263 Template = class(Node)
265 function Template.__init__(self, template)
267 self.template = template
270 function Template.render(self)
271 luci.template.render(self.template, {self=self})
276 Map - A map describing a configuration file
280 function Map.__init__(self, config, ...)
281 Node.__init__(self, ...)
282 Node._i18n(self, config, nil, nil, ...)
285 self.parsechain = {self.config}
286 self.template = "cbi/map"
287 self.apply_on_parse = nil
288 self.uci = uci.cursor()
290 if not self.uci:load(self.config) then
291 error("Unable to read UCI data: " .. self.config)
294 self.validator = luci.uvl.UVL()
295 self.scheme = self.validator:get_scheme(self.config)
299 function Map.get_scheme(self, sectiontype, option)
301 return self.scheme and self.scheme.sections[sectiontype]
303 return self.scheme and self.scheme.variables[sectiontype]
304 and self.scheme.variables[sectiontype][option]
308 function Map.submitstate(self)
309 return luci.http.formvalue("cbi.submit")
312 -- Chain foreign config
313 function Map.chain(self, config)
314 table.insert(self.parsechain, config)
317 -- Use optimized UCI writing
318 function Map.parse(self)
322 for i, config in ipairs(self.parsechain) do
323 self.uci:save(config)
325 if self:submitstate() and (self.autoapply or luci.http.formvalue("cbi.apply")) then
326 for i, config in ipairs(self.parsechain) do
327 self.uci:commit(config)
329 -- Refresh data because commit changes section names
330 self.uci:load(config)
332 if self.apply_on_parse then
333 self.uci:apply(self.parsechain)
335 self._apply = function()
336 local cmd = self.uci:apply(self.parsechain, true)
342 Node.parse(self, true)
345 for i, config in ipairs(self.parsechain) do
346 self.uci:unload(config)
348 if type(self.commit_handler) == "function" then
349 self:commit_handler(self:submitstate())
353 if self:submitstate() then
355 self.state = self.changed and FORM_CHANGED or FORM_VALID
357 self.state = FORM_INVALID
360 self.state = FORM_NODATA
366 function Map.render(self, ...)
367 Node.render(self, ...)
369 local fp = self._apply()
375 -- Creates a child section
376 function Map.section(self, class, ...)
377 if instanceof(class, AbstractSection) then
378 local obj = class(self, ...)
382 error("class must be a descendent of AbstractSection")
387 function Map.add(self, sectiontype)
388 return self.uci:add(self.config, sectiontype)
392 function Map.set(self, section, option, value)
394 return self.uci:set(self.config, section, option, value)
396 return self.uci:set(self.config, section, value)
401 function Map.del(self, section, option)
403 return self.uci:delete(self.config, section, option)
405 return self.uci:delete(self.config, section)
410 function Map.get(self, section, option)
412 return self.uci:get_all(self.config)
414 return self.uci:get(self.config, section, option)
416 return self.uci:get_all(self.config, section)
426 Page.__init__ = Node.__init__
427 Page.parse = function() end
431 SimpleForm - A Simple non-UCI form
433 SimpleForm = class(Node)
435 function SimpleForm.__init__(self, config, title, description, data)
436 Node.__init__(self, title, description)
438 self.data = data or {}
439 self.template = "cbi/simpleform"
441 self.pageaction = false
444 function SimpleForm.parse(self, ...)
445 if luci.http.formvalue("cbi.submit") then
446 Node.parse(self, 1, ...)
450 for k, j in ipairs(self.children) do
451 for i, v in ipairs(j.children) do
453 and (not v.tag_missing or not v.tag_missing[1])
454 and (not v.tag_invalid or not v.tag_invalid[1])
459 not self:submitstate() and FORM_NODATA
460 or valid and FORM_VALID
463 self.dorender = not self.handle or self:handle(state, self.data) ~= false
467 function SimpleForm.render(self, ...)
468 if self.dorender then
469 Node.render(self, ...)
473 function SimpleForm.submitstate(self)
474 return luci.http.formvalue("cbi.submit")
477 function SimpleForm.section(self, class, ...)
478 if instanceof(class, AbstractSection) then
479 local obj = class(self, ...)
483 error("class must be a descendent of AbstractSection")
487 -- Creates a child field
488 function SimpleForm.field(self, class, ...)
490 for k, v in ipairs(self.children) do
491 if instanceof(v, SimpleSection) then
497 section = self:section(SimpleSection)
500 if instanceof(class, AbstractValue) then
501 local obj = class(self, section, ...)
502 obj.track_missing = true
506 error("class must be a descendent of AbstractValue")
510 function SimpleForm.set(self, section, option, value)
511 self.data[option] = value
515 function SimpleForm.del(self, section, option)
516 self.data[option] = nil
520 function SimpleForm.get(self, section, option)
521 return self.data[option]
525 function SimpleForm.get_scheme()
534 AbstractSection = class(Node)
536 function AbstractSection.__init__(self, map, sectiontype, ...)
537 Node.__init__(self, ...)
538 self.sectiontype = sectiontype
540 self.config = map.config
545 self.tag_invalid = {}
546 self.tag_deperror = {}
549 self.addremove = false
553 -- Appends a new option
554 function AbstractSection.option(self, class, option, ...)
555 -- Autodetect from UVL
556 if class == true and self.map:get_scheme(self.sectiontype, option) then
557 local vs = self.map:get_scheme(self.sectiontype, option)
558 if vs.type == "boolean" then
560 elseif vs.type == "list" then
562 elseif vs.type == "enum" or vs.type == "reference" then
569 if instanceof(class, AbstractValue) then
570 local obj = class(self.map, self, option, ...)
572 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
575 self.fields[option] = obj
577 elseif class == true then
578 error("No valid class was given and autodetection failed.")
580 error("class must be a descendant of AbstractValue")
584 -- Parse optional options
585 function AbstractSection.parse_optionals(self, section)
586 if not self.optional then
590 self.optionals[section] = {}
592 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
593 for k,v in ipairs(self.children) do
594 if v.optional and not v:cfgvalue(section) then
595 if field == v.option then
598 table.insert(self.optionals[section], v)
603 if field and #field > 0 and self.dynamic then
604 self:add_dynamic(field)
608 -- Add a dynamic option
609 function AbstractSection.add_dynamic(self, field, optional)
610 local o = self:option(Value, field, field)
611 o.optional = optional
614 -- Parse all dynamic options
615 function AbstractSection.parse_dynamic(self, section)
616 if not self.dynamic then
620 local arr = luci.util.clone(self:cfgvalue(section))
621 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
622 for k, v in pairs(form) do
626 for key,val in pairs(arr) do
629 for i,c in ipairs(self.children) do
630 if c.option == key then
635 if create and key:sub(1, 1) ~= "." then
636 self:add_dynamic(key, true)
641 -- Returns the section's UCI table
642 function AbstractSection.cfgvalue(self, section)
643 return self.map:get(section)
646 -- Removes the section
647 function AbstractSection.remove(self, section)
648 return self.map:del(section)
651 -- Creates the section
652 function AbstractSection.create(self, section)
656 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
658 section = self.map:add(self.sectiontype)
663 for k,v in pairs(self.children) do
665 self.map:set(section, v.option, v.default)
669 for k,v in pairs(self.defaults) do
670 self.map:set(section, k, v)
678 SimpleSection = class(AbstractSection)
680 function SimpleSection.__init__(self, form, ...)
681 AbstractSection.__init__(self, form, nil, ...)
682 self.template = "cbi/nullsection"
686 Table = class(AbstractSection)
688 function Table.__init__(self, form, data, ...)
689 local datasource = {}
690 datasource.config = "table"
693 function datasource.get(self, section, option)
694 return data[section] and data[section][option]
697 function datasource.submitstate(self)
698 return luci.http.formvalue("cbi.submit")
701 function datasource.del(...)
705 function datasource.get_scheme()
709 AbstractSection.__init__(self, datasource, "table", ...)
710 self.template = "cbi/tblsection"
711 self.rowcolors = true
712 self.anonymous = true
715 function Table.parse(self)
716 for i, k in ipairs(self:cfgsections()) do
717 if self.map:submitstate() then
723 function Table.cfgsections(self)
726 for i, v in luci.util.kspairs(self.data) do
727 table.insert(sections, i)
736 NamedSection - A fixed configuration section defined by its name
738 NamedSection = class(AbstractSection)
740 function NamedSection.__init__(self, map, section, stype, ...)
741 AbstractSection.__init__(self, map, stype, ...)
742 Node._i18n(self, map.config, section, nil, ...)
745 self.addremove = false
747 -- Use defaults from UVL
748 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
749 local vs = self.map:get_scheme(self.sectiontype)
750 self.addremove = not vs.unique and not vs.required
751 self.dynamic = vs.dynamic
752 self.title = self.title or vs.title
753 self.description = self.description or vs.descr
756 self.template = "cbi/nsection"
757 self.section = section
760 function NamedSection.parse(self, novld)
761 local s = self.section
762 local active = self:cfgvalue(s)
764 if self.addremove then
765 local path = self.config.."."..s
766 if active then -- Remove the section
767 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
770 else -- Create and apply default values
771 if luci.http.formvalue("cbi.cns."..path) then
779 AbstractSection.parse_dynamic(self, s)
780 if self.map:submitstate() then
783 if not novld and not self.override_scheme and self.map.scheme then
784 _uvl_validate_section(self, s)
787 AbstractSection.parse_optionals(self, s)
793 TypedSection - A (set of) configuration section(s) defined by the type
794 addremove: Defines whether the user can add/remove sections of this type
795 anonymous: Allow creating anonymous sections
796 validate: a validation function returning nil if the section is invalid
798 TypedSection = class(AbstractSection)
800 function TypedSection.__init__(self, map, type, ...)
801 AbstractSection.__init__(self, map, type, ...)
802 Node._i18n(self, map.config, type, nil, ...)
804 self.template = "cbi/tsection"
806 self.anonymous = false
808 -- Use defaults from UVL
809 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
810 local vs = self.map:get_scheme(self.sectiontype)
811 self.addremove = not vs.unique and not vs.required
812 self.dynamic = vs.dynamic
813 self.anonymous = not vs.named
814 self.title = self.title or vs.title
815 self.description = self.description or vs.descr
819 -- Return all matching UCI sections for this TypedSection
820 function TypedSection.cfgsections(self)
822 self.map.uci:foreach(self.map.config, self.sectiontype,
824 if self:checkscope(section[".name"]) then
825 table.insert(sections, section[".name"])
832 -- Limits scope to sections that have certain option => value pairs
833 function TypedSection.depends(self, option, value)
834 table.insert(self.deps, {option=option, value=value})
837 function TypedSection.parse(self, novld)
838 if self.addremove then
840 local crval = REMOVE_PREFIX .. self.config
841 local name = luci.http.formvaluetable(crval)
842 for k,v in pairs(name) do
843 if k:sub(-2) == ".x" then
846 if self:cfgvalue(k) and self:checkscope(k) then
853 for i, k in ipairs(self:cfgsections()) do
854 AbstractSection.parse_dynamic(self, k)
855 if self.map:submitstate() then
858 if not novld and not self.override_scheme and self.map.scheme then
859 _uvl_validate_section(self, k)
862 AbstractSection.parse_optionals(self, k)
865 if self.addremove then
868 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
869 local name = luci.http.formvalue(crval)
870 if self.anonymous then
872 created = self:create()
876 -- Ignore if it already exists
877 if self:cfgvalue(name) then
881 name = self:checkscope(name)
884 self.err_invalid = true
887 if name and #name > 0 then
888 created = self:create(name) and name
890 self.invalid_cts = true
897 AbstractSection.parse_optionals(self, created)
902 -- Verifies scope of sections
903 function TypedSection.checkscope(self, section)
904 -- Check if we are not excluded
905 if self.filter and not self:filter(section) then
909 -- Check if at least one dependency is met
910 if #self.deps > 0 and self:cfgvalue(section) then
913 for k, v in ipairs(self.deps) do
914 if self:cfgvalue(section)[v.option] == v.value then
924 return self:validate(section)
928 -- Dummy validate function
929 function TypedSection.validate(self, section)
935 AbstractValue - An abstract Value Type
936 null: Value can be empty
937 valid: A function returning the value if it is valid otherwise nil
938 depends: A table of option => value pairs of which one must be true
939 default: The default value
940 size: The size of the input fields
941 rmempty: Unset value if empty
942 optional: This value is optional (see AbstractSection.optionals)
944 AbstractValue = class(Node)
946 function AbstractValue.__init__(self, map, section, option, ...)
947 Node.__init__(self, ...)
948 self.section = section
951 self.config = map.config
952 self.tag_invalid = {}
953 self.tag_missing = {}
954 self.tag_reqerror = {}
957 --self.cast = "string"
959 self.track_missing = false
960 --self.rmempty = false
963 self.optional = false
966 function AbstractValue.prepare(self)
967 -- Use defaults from UVL
968 if not self.override_scheme
969 and self.map:get_scheme(self.section.sectiontype, self.option) then
970 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
971 if self.rmempty == nil then
972 self.rmempty = not vs.required
974 if self.cast == nil then
975 self.cast = (vs.type == "list") and "list" or "string"
977 self.title = self.title or vs.title
978 self.description = self.description or vs.descr
979 if self.default == nil then
980 self.default = vs.default
983 if vs.depends and not self.override_dependencies then
984 for i, deps in ipairs(vs.depends) do
985 deps = _uvl_strip_remote_dependencies(deps)
993 self.cast = self.cast or "string"
996 -- Add a dependencie to another section field
997 function AbstractValue.depends(self, field, value)
999 if type(field) == "string" then
1006 table.insert(self.deps, {deps=deps, add=""})
1009 -- Generates the unique CBID
1010 function AbstractValue.cbid(self, section)
1011 return "cbid."..self.map.config.."."..section.."."..self.option
1014 -- Return whether this object should be created
1015 function AbstractValue.formcreated(self, section)
1016 local key = "cbi.opt."..self.config.."."..section
1017 return (luci.http.formvalue(key) == self.option)
1020 -- Returns the formvalue for this object
1021 function AbstractValue.formvalue(self, section)
1022 return luci.http.formvalue(self:cbid(section))
1025 function AbstractValue.additional(self, value)
1026 self.optional = value
1029 function AbstractValue.mandatory(self, value)
1030 self.rmempty = not value
1033 function AbstractValue.parse(self, section)
1034 local fvalue = self:formvalue(section)
1035 local cvalue = self:cfgvalue(section)
1037 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1038 fvalue = self:transform(self:validate(fvalue, section))
1040 self.tag_invalid[section] = true
1042 if fvalue and not (fvalue == cvalue) then
1043 self:write(section, fvalue)
1045 else -- Unset the UCI or error
1046 if self.rmempty or self.optional then
1047 self:remove(section)
1048 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1049 self.tag_missing[section] = true
1054 -- Render if this value exists or if it is mandatory
1055 function AbstractValue.render(self, s, scope)
1056 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1059 scope.cbid = self:cbid(s)
1060 scope.striptags = luci.util.striptags
1062 scope.ifattr = function(cond,key,val)
1064 return string.format(
1065 ' %s="%s"', tostring(key),
1066 luci.util.pcdata(tostring( val
1068 or (type(self[key]) ~= "function" and self[key])
1076 scope.attr = function(...)
1077 return scope.ifattr( true, ... )
1080 Node.render(self, scope)
1084 -- Return the UCI value of this object
1085 function AbstractValue.cfgvalue(self, section)
1086 local value = self.map:get(section, self.option)
1089 elseif not self.cast or self.cast == type(value) then
1091 elseif self.cast == "string" then
1092 if type(value) == "table" then
1095 elseif self.cast == "table" then
1096 return luci.util.split(value, "%s+", nil, true)
1100 -- Validate the form value
1101 function AbstractValue.validate(self, value)
1105 AbstractValue.transform = AbstractValue.validate
1109 function AbstractValue.write(self, section, value)
1110 return self.map:set(section, self.option, value)
1114 function AbstractValue.remove(self, section)
1115 return self.map:del(section, self.option)
1122 Value - A one-line value
1123 maxlength: The maximum length
1125 Value = class(AbstractValue)
1127 function Value.__init__(self, ...)
1128 AbstractValue.__init__(self, ...)
1129 self.template = "cbi/value"
1134 function Value.value(self, key, val)
1136 table.insert(self.keylist, tostring(key))
1137 table.insert(self.vallist, tostring(val))
1141 -- DummyValue - This does nothing except being there
1142 DummyValue = class(AbstractValue)
1144 function DummyValue.__init__(self, ...)
1145 AbstractValue.__init__(self, ...)
1146 self.template = "cbi/dvalue"
1150 function DummyValue.cfgvalue(self, section)
1153 if type(self.value) == "function" then
1154 value = self:value(section)
1159 value = AbstractValue.cfgvalue(self, section)
1164 function DummyValue.parse(self)
1170 Flag - A flag being enabled or disabled
1172 Flag = class(AbstractValue)
1174 function Flag.__init__(self, ...)
1175 AbstractValue.__init__(self, ...)
1176 self.template = "cbi/fvalue"
1182 -- A flag can only have two states: set or unset
1183 function Flag.parse(self, section)
1184 local fvalue = self:formvalue(section)
1187 fvalue = self.enabled
1189 fvalue = self.disabled
1192 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1193 if not(fvalue == self:cfgvalue(section)) then
1194 self:write(section, fvalue)
1197 self:remove(section)
1204 ListValue - A one-line value predefined in a list
1205 widget: The widget that will be used (select, radio)
1207 ListValue = class(AbstractValue)
1209 function ListValue.__init__(self, ...)
1210 AbstractValue.__init__(self, ...)
1211 self.template = "cbi/lvalue"
1216 self.widget = "select"
1219 function ListValue.prepare(self, ...)
1220 AbstractValue.prepare(self, ...)
1221 if not self.override_scheme
1222 and self.map:get_scheme(self.section.sectiontype, self.option) then
1223 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1224 if self.value and vs.valuelist and not self.override_values then
1225 for k, v in ipairs(vs.valuelist) do
1227 if not self.override_dependencies
1228 and vs.enum_depends and vs.enum_depends[v.value] then
1229 for i, dep in ipairs(vs.enum_depends[v.value]) do
1230 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1233 self:value(v.value, v.title or v.value, unpack(deps))
1239 function ListValue.value(self, key, val, ...)
1240 if luci.util.contains(self.keylist, key) then
1245 table.insert(self.keylist, tostring(key))
1246 table.insert(self.vallist, tostring(val))
1248 for i, deps in ipairs({...}) do
1249 table.insert(self.deps, {add = "-"..key, deps=deps})
1253 function ListValue.validate(self, val)
1254 if luci.util.contains(self.keylist, val) then
1264 MultiValue - Multiple delimited values
1265 widget: The widget that will be used (select, checkbox)
1266 delimiter: The delimiter that will separate the values (default: " ")
1268 MultiValue = class(AbstractValue)
1270 function MultiValue.__init__(self, ...)
1271 AbstractValue.__init__(self, ...)
1272 self.template = "cbi/mvalue"
1277 self.widget = "checkbox"
1278 self.delimiter = " "
1281 function MultiValue.render(self, ...)
1282 if self.widget == "select" and not self.size then
1283 self.size = #self.vallist
1286 AbstractValue.render(self, ...)
1289 function MultiValue.value(self, key, val)
1290 if luci.util.contains(self.keylist, key) then
1295 table.insert(self.keylist, tostring(key))
1296 table.insert(self.vallist, tostring(val))
1299 function MultiValue.valuelist(self, section)
1300 local val = self:cfgvalue(section)
1302 if not(type(val) == "string") then
1306 return luci.util.split(val, self.delimiter)
1309 function MultiValue.validate(self, val)
1310 val = (type(val) == "table") and val or {val}
1314 for i, value in ipairs(val) do
1315 if luci.util.contains(self.keylist, value) then
1316 result = result and (result .. self.delimiter .. value) or value
1324 StaticList = class(MultiValue)
1326 function StaticList.__init__(self, ...)
1327 MultiValue.__init__(self, ...)
1329 self.valuelist = self.cfgvalue
1331 if not self.override_scheme
1332 and self.map:get_scheme(self.section.sectiontype, self.option) then
1333 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1334 if self.value and vs.values and not self.override_values then
1335 for k, v in pairs(vs.values) do
1342 function StaticList.validate(self, value)
1343 value = (type(value) == "table") and value or {value}
1346 for i, v in ipairs(value) do
1347 if luci.util.contains(self.vallist, v) then
1348 table.insert(valid, v)
1355 DynamicList = class(AbstractValue)
1357 function DynamicList.__init__(self, ...)
1358 AbstractValue.__init__(self, ...)
1359 self.template = "cbi/dynlist"
1365 function DynamicList.value(self, key, val)
1367 table.insert(self.keylist, tostring(key))
1368 table.insert(self.vallist, tostring(val))
1371 function DynamicList.formvalue(self, section)
1372 local value = AbstractValue.formvalue(self, section)
1373 value = (type(value) == "table") and value or {value}
1376 for i, v in ipairs(value) do
1378 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1379 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1380 table.insert(valid, v)
1389 TextValue - A multi-line value
1392 TextValue = class(AbstractValue)
1394 function TextValue.__init__(self, ...)
1395 AbstractValue.__init__(self, ...)
1396 self.template = "cbi/tvalue"
1402 Button = class(AbstractValue)
1404 function Button.__init__(self, ...)
1405 AbstractValue.__init__(self, ...)
1406 self.template = "cbi/button"
1407 self.inputstyle = nil
1412 FileUpload = class(AbstractValue)
1414 function FileUpload.__init__(self, ...)
1415 AbstractValue.__init__(self, ...)
1416 self.template = "cbi/upload"
1417 if not self.map.upload_fields then
1418 self.map.upload_fields = { self }
1420 self.map.upload_fields[#self.map.upload_fields+1] = self
1424 function FileUpload.formcreated(self, section)
1425 return AbstractValue.formcreated(self, section) or
1426 luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
1427 luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1430 function FileUpload.cfgvalue(self, section)
1431 local val = AbstractValue.cfgvalue(self, section)
1432 if val and luci.fs.access(val) then
1438 function FileUpload.formvalue(self, section)
1439 local val = AbstractValue.formvalue(self, section)
1441 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1442 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1452 function FileUpload.remove(self, section)
1453 local val = AbstractValue.formvalue(self, section)
1454 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1455 return AbstractValue.remove(self, section)
1459 FileBrowser = class(AbstractValue)
1461 function FileBrowser.__init__(self, ...)
1462 AbstractValue.__init__(self, ...)
1463 self.template = "cbi/browser"