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
45 CREATE_PREFIX = "cbi.cts."
46 REMOVE_PREFIX = "cbi.rts."
48 -- Loads a CBI map from given file, creating an environment and returns it
49 function load(cbimap, ...)
51 local i18n = require "luci.i18n"
52 require("luci.config")
55 local upldir = "/lib/uci/upload/"
56 local cbidir = luci.util.libpath() .. "/model/cbi/"
57 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
60 luci.i18n.loadc("cbi")
61 luci.i18n.loadc("uvl")
64 translate=i18n.translate,
65 translatef=i18n.translatef,
69 setfenv(func, setmetatable(env, {__index =
71 return rawget(tbl, key) or _M[key] or _G[key]
74 local maps = { func() }
76 local has_upload = false
78 for i, map in ipairs(maps) do
79 if not instanceof(map, Node) then
80 error("CBI map returns no valid map object!")
84 if map.upload_fields then
86 for _, field in ipairs(map.upload_fields) do
88 field.config .. '.' ..
89 field.section.sectiontype .. '.' ..
98 local uci = luci.model.uci.cursor()
99 local prm = luci.http.context.request.message.params
102 luci.http.setfilehandler(
103 function( field, chunk, eof )
104 if not field then return end
105 if field.name and not cbid then
106 local c, s, o = field.name:gmatch(
107 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
110 if c and s and o then
111 local t = uci:get( c, s )
112 if t and uploads[c.."."..t.."."..o] then
113 local path = upldir .. field.name
114 fd = io.open(path, "w")
123 if field.name == cbid and fd then
139 local function _uvl_validate_section(node, name)
140 local co = node.map:get()
142 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
143 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
145 local function tag_fields(e)
146 if e.option and node.fields[e.option] then
147 if node.fields[e.option].error then
148 node.fields[e.option].error[name] = e
150 node.fields[e.option].error = { [name] = e }
153 for _, c in ipairs(e.childs) do tag_fields(c) end
157 local function tag_section(e)
159 for _, c in ipairs(e.childs) do
160 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
161 table.insert( s, c.childs[1]:string() )
163 table.insert( s, c:string() )
170 node.error = { [name] = s }
175 local stat, err = node.map.validator:validate_section(node.config, name, co)
177 node.map.save = false
184 local function _uvl_strip_remote_dependencies(deps)
187 for k, v in pairs(deps) do
188 k = k:gsub("%$config%.%$section%.", "")
189 if k:match("^[%w_]+$") and type(v) == "string" then
198 -- Node pseudo abstract class
201 function Node.__init__(self, title, description)
203 self.title = title or ""
204 self.description = description or ""
205 self.template = "cbi/node"
209 function Node._i18n(self, config, section, option, title, description)
212 if type(luci.i18n) == "table" then
214 local key = config and config:gsub("[^%w]+", "") or ""
216 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
217 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
219 self.title = title or luci.i18n.translate( key, option or section or config )
220 self.description = description or luci.i18n.translate( key .. "_desc", "" )
225 function Node.prepare(self, ...)
226 for k, child in ipairs(self.children) do
231 -- Append child nodes
232 function Node.append(self, obj)
233 table.insert(self.children, obj)
236 -- Parse this node and its children
237 function Node.parse(self, ...)
238 for k, child in ipairs(self.children) do
244 function Node.render(self, scope)
248 luci.template.render(self.template, scope)
251 -- Render the children
252 function Node.render_children(self, ...)
253 for k, node in ipairs(self.children) do
260 A simple template element
262 Template = class(Node)
264 function Template.__init__(self, template)
266 self.template = template
269 function Template.render(self)
270 luci.template.render(self.template, {self=self})
275 Map - A map describing a configuration file
279 function Map.__init__(self, config, ...)
280 Node.__init__(self, ...)
281 Node._i18n(self, config, nil, nil, ...)
284 self.parsechain = {self.config}
285 self.template = "cbi/map"
286 self.apply_on_parse = nil
287 self.uci = uci.cursor()
289 if not self.uci:load(self.config) then
290 error("Unable to read UCI data: " .. self.config)
293 self.validator = luci.uvl.UVL()
294 self.scheme = self.validator:get_scheme(self.config)
298 function Map.get_scheme(self, sectiontype, option)
300 return self.scheme and self.scheme.sections[sectiontype]
302 return self.scheme and self.scheme.variables[sectiontype]
303 and self.scheme.variables[sectiontype][option]
308 -- Chain foreign config
309 function Map.chain(self, config)
310 table.insert(self.parsechain, config)
313 -- Use optimized UCI writing
314 function Map.parse(self)
318 for i, config in ipairs(self.parsechain) do
319 self.uci:save(config)
321 if luci.http.formvalue("cbi.apply") then
322 for i, config in ipairs(self.parsechain) do
323 self.uci:commit(config)
325 -- Refresh data because commit changes section names
326 self.uci:load(config)
328 if self.apply_on_parse then
329 self.uci:apply(self.parsechain)
331 self._apply = function()
332 local cmd = self.uci:apply(self.parsechain, true)
338 Node.parse(self, true)
341 for i, config in ipairs(self.parsechain) do
342 self.uci:unload(config)
344 if type(self.commit_handler) == "function" then
345 self:commit_handler()
350 function Map.render(self, ...)
351 Node.render(self, ...)
353 local fp = self._apply()
359 -- Creates a child section
360 function Map.section(self, class, ...)
361 if instanceof(class, AbstractSection) then
362 local obj = class(self, ...)
366 error("class must be a descendent of AbstractSection")
371 function Map.add(self, sectiontype)
372 return self.uci:add(self.config, sectiontype)
376 function Map.set(self, section, option, value)
378 return self.uci:set(self.config, section, option, value)
380 return self.uci:set(self.config, section, value)
385 function Map.del(self, section, option)
387 return self.uci:delete(self.config, section, option)
389 return self.uci:delete(self.config, section)
394 function Map.get(self, section, option)
396 return self.uci:get_all(self.config)
398 return self.uci:get(self.config, section, option)
400 return self.uci:get_all(self.config, section)
410 Page.__init__ = Node.__init__
411 Page.parse = function() end
415 SimpleForm - A Simple non-UCI form
417 SimpleForm = class(Node)
419 function SimpleForm.__init__(self, config, title, description, data)
420 Node.__init__(self, title, description)
422 self.data = data or {}
423 self.template = "cbi/simpleform"
427 function SimpleForm.parse(self, ...)
428 if luci.http.formvalue("cbi.submit") then
429 Node.parse(self, 1, ...)
433 for k, j in ipairs(self.children) do
434 for i, v in ipairs(j.children) do
436 and (not v.tag_missing or not v.tag_missing[1])
437 and (not v.tag_invalid or not v.tag_invalid[1])
442 not luci.http.formvalue("cbi.submit") and 0
446 self.dorender = not self.handle or self:handle(state, self.data) ~= false
449 function SimpleForm.render(self, ...)
450 if self.dorender then
451 Node.render(self, ...)
455 function SimpleForm.section(self, class, ...)
456 if instanceof(class, AbstractSection) then
457 local obj = class(self, ...)
461 error("class must be a descendent of AbstractSection")
465 -- Creates a child field
466 function SimpleForm.field(self, class, ...)
468 for k, v in ipairs(self.children) do
469 if instanceof(v, SimpleSection) then
475 section = self:section(SimpleSection)
478 if instanceof(class, AbstractValue) then
479 local obj = class(self, section, ...)
480 obj.track_missing = true
484 error("class must be a descendent of AbstractValue")
488 function SimpleForm.set(self, section, option, value)
489 self.data[option] = value
493 function SimpleForm.del(self, section, option)
494 self.data[option] = nil
498 function SimpleForm.get(self, section, option)
499 return self.data[option]
503 function SimpleForm.get_scheme()
512 AbstractSection = class(Node)
514 function AbstractSection.__init__(self, map, sectiontype, ...)
515 Node.__init__(self, ...)
516 self.sectiontype = sectiontype
518 self.config = map.config
523 self.tag_invalid = {}
524 self.tag_deperror = {}
527 self.addremove = false
531 -- Appends a new option
532 function AbstractSection.option(self, class, option, ...)
533 -- Autodetect from UVL
534 if class == true and self.map:get_scheme(self.sectiontype, option) then
535 local vs = self.map:get_scheme(self.sectiontype, option)
536 if vs.type == "boolean" then
538 elseif vs.type == "list" then
540 elseif vs.type == "enum" or vs.type == "reference" then
547 if instanceof(class, AbstractValue) then
548 local obj = class(self.map, self, option, ...)
550 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
553 self.fields[option] = obj
555 elseif class == true then
556 error("No valid class was given and autodetection failed.")
558 error("class must be a descendant of AbstractValue")
562 -- Parse optional options
563 function AbstractSection.parse_optionals(self, section)
564 if not self.optional then
568 self.optionals[section] = {}
570 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
571 for k,v in ipairs(self.children) do
572 if v.optional and not v:cfgvalue(section) then
573 if field == v.option then
576 table.insert(self.optionals[section], v)
581 if field and #field > 0 and self.dynamic then
582 self:add_dynamic(field)
586 -- Add a dynamic option
587 function AbstractSection.add_dynamic(self, field, optional)
588 local o = self:option(Value, field, field)
589 o.optional = optional
592 -- Parse all dynamic options
593 function AbstractSection.parse_dynamic(self, section)
594 if not self.dynamic then
598 local arr = luci.util.clone(self:cfgvalue(section))
599 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
600 for k, v in pairs(form) do
604 for key,val in pairs(arr) do
607 for i,c in ipairs(self.children) do
608 if c.option == key then
613 if create and key:sub(1, 1) ~= "." then
614 self:add_dynamic(key, true)
619 -- Returns the section's UCI table
620 function AbstractSection.cfgvalue(self, section)
621 return self.map:get(section)
624 -- Removes the section
625 function AbstractSection.remove(self, section)
626 return self.map:del(section)
629 -- Creates the section
630 function AbstractSection.create(self, section)
634 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
636 section = self.map:add(self.sectiontype)
641 for k,v in pairs(self.children) do
643 self.map:set(section, v.option, v.default)
647 for k,v in pairs(self.defaults) do
648 self.map:set(section, k, v)
656 SimpleSection = class(AbstractSection)
658 function SimpleSection.__init__(self, form, ...)
659 AbstractSection.__init__(self, form, nil, ...)
660 self.template = "cbi/nullsection"
664 Table = class(AbstractSection)
666 function Table.__init__(self, form, data, ...)
667 local datasource = {}
668 datasource.config = "table"
671 function datasource.get(self, section, option)
672 return data[section] and data[section][option]
675 function datasource.del(...)
679 function datasource.get_scheme()
683 AbstractSection.__init__(self, datasource, "table", ...)
684 self.template = "cbi/tblsection"
685 self.rowcolors = true
686 self.anonymous = true
689 function Table.parse(self)
690 for i, k in ipairs(self:cfgsections()) do
691 if luci.http.formvalue("cbi.submit") then
697 function Table.cfgsections(self)
700 for i, v in luci.util.kspairs(self.data) do
701 table.insert(sections, i)
710 NamedSection - A fixed configuration section defined by its name
712 NamedSection = class(AbstractSection)
714 function NamedSection.__init__(self, map, section, stype, ...)
715 AbstractSection.__init__(self, map, stype, ...)
716 Node._i18n(self, map.config, section, nil, ...)
719 self.addremove = false
721 -- Use defaults from UVL
722 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
723 local vs = self.map:get_scheme(self.sectiontype)
724 self.addremove = not vs.unique and not vs.required
725 self.dynamic = vs.dynamic
726 self.title = self.title or vs.title
727 self.description = self.description or vs.descr
730 self.template = "cbi/nsection"
731 self.section = section
734 function NamedSection.parse(self, novld)
735 local s = self.section
736 local active = self:cfgvalue(s)
738 if self.addremove then
739 local path = self.config.."."..s
740 if active then -- Remove the section
741 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
744 else -- Create and apply default values
745 if luci.http.formvalue("cbi.cns."..path) then
753 AbstractSection.parse_dynamic(self, s)
754 if luci.http.formvalue("cbi.submit") then
757 if not novld and not self.override_scheme and self.map.scheme then
758 _uvl_validate_section(self, s)
761 AbstractSection.parse_optionals(self, s)
767 TypedSection - A (set of) configuration section(s) defined by the type
768 addremove: Defines whether the user can add/remove sections of this type
769 anonymous: Allow creating anonymous sections
770 validate: a validation function returning nil if the section is invalid
772 TypedSection = class(AbstractSection)
774 function TypedSection.__init__(self, map, type, ...)
775 AbstractSection.__init__(self, map, type, ...)
776 Node._i18n(self, map.config, type, nil, ...)
778 self.template = "cbi/tsection"
780 self.anonymous = false
782 -- Use defaults from UVL
783 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
784 local vs = self.map:get_scheme(self.sectiontype)
785 self.addremove = not vs.unique and not vs.required
786 self.dynamic = vs.dynamic
787 self.anonymous = not vs.named
788 self.title = self.title or vs.title
789 self.description = self.description or vs.descr
793 -- Return all matching UCI sections for this TypedSection
794 function TypedSection.cfgsections(self)
796 self.map.uci:foreach(self.map.config, self.sectiontype,
798 if self:checkscope(section[".name"]) then
799 table.insert(sections, section[".name"])
806 -- Limits scope to sections that have certain option => value pairs
807 function TypedSection.depends(self, option, value)
808 table.insert(self.deps, {option=option, value=value})
811 function TypedSection.parse(self, novld)
812 if self.addremove then
814 local crval = REMOVE_PREFIX .. self.config
815 local name = luci.http.formvaluetable(crval)
816 for k,v in pairs(name) do
817 if k:sub(-2) == ".x" then
820 if self:cfgvalue(k) and self:checkscope(k) then
827 for i, k in ipairs(self:cfgsections()) do
828 AbstractSection.parse_dynamic(self, k)
829 if luci.http.formvalue("cbi.submit") then
832 if not novld and not self.override_scheme and self.map.scheme then
833 _uvl_validate_section(self, k)
836 AbstractSection.parse_optionals(self, k)
839 if self.addremove then
842 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
843 local name = luci.http.formvalue(crval)
844 if self.anonymous then
846 created = self:create()
850 -- Ignore if it already exists
851 if self:cfgvalue(name) then
855 name = self:checkscope(name)
858 self.err_invalid = true
861 if name and #name > 0 then
862 created = self:create(name) and name
864 self.invalid_cts = true
871 AbstractSection.parse_optionals(self, created)
876 -- Verifies scope of sections
877 function TypedSection.checkscope(self, section)
878 -- Check if we are not excluded
879 if self.filter and not self:filter(section) then
883 -- Check if at least one dependency is met
884 if #self.deps > 0 and self:cfgvalue(section) then
887 for k, v in ipairs(self.deps) do
888 if self:cfgvalue(section)[v.option] == v.value then
898 return self:validate(section)
902 -- Dummy validate function
903 function TypedSection.validate(self, section)
909 AbstractValue - An abstract Value Type
910 null: Value can be empty
911 valid: A function returning the value if it is valid otherwise nil
912 depends: A table of option => value pairs of which one must be true
913 default: The default value
914 size: The size of the input fields
915 rmempty: Unset value if empty
916 optional: This value is optional (see AbstractSection.optionals)
918 AbstractValue = class(Node)
920 function AbstractValue.__init__(self, map, section, option, ...)
921 Node.__init__(self, ...)
922 self.section = section
925 self.config = map.config
926 self.tag_invalid = {}
927 self.tag_missing = {}
928 self.tag_reqerror = {}
931 --self.cast = "string"
933 self.track_missing = false
934 --self.rmempty = false
937 self.optional = false
940 function AbstractValue.prepare(self)
941 -- Use defaults from UVL
942 if not self.override_scheme
943 and self.map:get_scheme(self.section.sectiontype, self.option) then
944 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
945 if self.rmempty == nil then
946 self.rmempty = not vs.required
948 if self.cast == nil then
949 self.cast = (vs.type == "list") and "list" or "string"
951 self.title = self.title or vs.title
952 self.description = self.description or vs.descr
953 if self.default == nil then
954 self.default = vs.default
957 if vs.depends and not self.override_dependencies then
958 for i, deps in ipairs(vs.depends) do
959 deps = _uvl_strip_remote_dependencies(deps)
967 self.cast = self.cast or "string"
970 -- Add a dependencie to another section field
971 function AbstractValue.depends(self, field, value)
973 if type(field) == "string" then
980 table.insert(self.deps, {deps=deps, add=""})
983 -- Generates the unique CBID
984 function AbstractValue.cbid(self, section)
985 return "cbid."..self.map.config.."."..section.."."..self.option
988 -- Return whether this object should be created
989 function AbstractValue.formcreated(self, section)
990 local key = "cbi.opt."..self.config.."."..section
991 return (luci.http.formvalue(key) == self.option)
994 -- Returns the formvalue for this object
995 function AbstractValue.formvalue(self, section)
996 return luci.http.formvalue(self:cbid(section))
999 function AbstractValue.additional(self, value)
1000 self.optional = value
1003 function AbstractValue.mandatory(self, value)
1004 self.rmempty = not value
1007 function AbstractValue.parse(self, section)
1008 local fvalue = self:formvalue(section)
1009 local cvalue = self:cfgvalue(section)
1011 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1012 fvalue = self:transform(self:validate(fvalue, section))
1014 self.tag_invalid[section] = true
1016 if fvalue and not (fvalue == cvalue) then
1017 self:write(section, fvalue)
1019 else -- Unset the UCI or error
1020 if self.rmempty or self.optional then
1021 self:remove(section)
1022 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1023 self.tag_missing[section] = true
1028 -- Render if this value exists or if it is mandatory
1029 function AbstractValue.render(self, s, scope)
1030 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1033 scope.cbid = self:cbid(s)
1034 scope.striptags = luci.util.striptags
1036 scope.ifattr = function(cond,key,val)
1038 return string.format(
1039 ' %s="%s"', tostring(key),
1040 luci.util.pcdata(tostring( val
1042 or (type(self[key]) ~= "function" and self[key])
1050 scope.attr = function(...)
1051 return scope.ifattr( true, ... )
1054 Node.render(self, scope)
1058 -- Return the UCI value of this object
1059 function AbstractValue.cfgvalue(self, section)
1060 local value = self.map:get(section, self.option)
1063 elseif not self.cast or self.cast == type(value) then
1065 elseif self.cast == "string" then
1066 if type(value) == "table" then
1069 elseif self.cast == "table" then
1070 return luci.util.split(value, "%s+", nil, true)
1074 -- Validate the form value
1075 function AbstractValue.validate(self, value)
1079 AbstractValue.transform = AbstractValue.validate
1083 function AbstractValue.write(self, section, value)
1084 return self.map:set(section, self.option, value)
1088 function AbstractValue.remove(self, section)
1089 return self.map:del(section, self.option)
1096 Value - A one-line value
1097 maxlength: The maximum length
1099 Value = class(AbstractValue)
1101 function Value.__init__(self, ...)
1102 AbstractValue.__init__(self, ...)
1103 self.template = "cbi/value"
1108 function Value.value(self, key, val)
1110 table.insert(self.keylist, tostring(key))
1111 table.insert(self.vallist, tostring(val))
1115 -- DummyValue - This does nothing except being there
1116 DummyValue = class(AbstractValue)
1118 function DummyValue.__init__(self, ...)
1119 AbstractValue.__init__(self, ...)
1120 self.template = "cbi/dvalue"
1124 function DummyValue.cfgvalue(self, section)
1127 if type(self.value) == "function" then
1128 value = self:value(section)
1133 value = AbstractValue.cfgvalue(self, section)
1138 function DummyValue.parse(self)
1144 Flag - A flag being enabled or disabled
1146 Flag = class(AbstractValue)
1148 function Flag.__init__(self, ...)
1149 AbstractValue.__init__(self, ...)
1150 self.template = "cbi/fvalue"
1156 -- A flag can only have two states: set or unset
1157 function Flag.parse(self, section)
1158 local fvalue = self:formvalue(section)
1161 fvalue = self.enabled
1163 fvalue = self.disabled
1166 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1167 if not(fvalue == self:cfgvalue(section)) then
1168 self:write(section, fvalue)
1171 self:remove(section)
1178 ListValue - A one-line value predefined in a list
1179 widget: The widget that will be used (select, radio)
1181 ListValue = class(AbstractValue)
1183 function ListValue.__init__(self, ...)
1184 AbstractValue.__init__(self, ...)
1185 self.template = "cbi/lvalue"
1190 self.widget = "select"
1193 function ListValue.prepare(self, ...)
1194 AbstractValue.prepare(self, ...)
1195 if not self.override_scheme
1196 and self.map:get_scheme(self.section.sectiontype, self.option) then
1197 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1198 if self.value and vs.valuelist and not self.override_values then
1199 for k, v in ipairs(vs.valuelist) do
1201 if not self.override_dependencies
1202 and vs.enum_depends and vs.enum_depends[v.value] then
1203 for i, dep in ipairs(vs.enum_depends[v.value]) do
1204 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1207 self:value(v.value, v.title or v.value, unpack(deps))
1213 function ListValue.value(self, key, val, ...)
1214 if luci.util.contains(self.keylist, key) then
1219 table.insert(self.keylist, tostring(key))
1220 table.insert(self.vallist, tostring(val))
1222 for i, deps in ipairs({...}) do
1223 table.insert(self.deps, {add = "-"..key, deps=deps})
1227 function ListValue.validate(self, val)
1228 if luci.util.contains(self.keylist, val) then
1238 MultiValue - Multiple delimited values
1239 widget: The widget that will be used (select, checkbox)
1240 delimiter: The delimiter that will separate the values (default: " ")
1242 MultiValue = class(AbstractValue)
1244 function MultiValue.__init__(self, ...)
1245 AbstractValue.__init__(self, ...)
1246 self.template = "cbi/mvalue"
1251 self.widget = "checkbox"
1252 self.delimiter = " "
1255 function MultiValue.render(self, ...)
1256 if self.widget == "select" and not self.size then
1257 self.size = #self.vallist
1260 AbstractValue.render(self, ...)
1263 function MultiValue.value(self, key, val)
1264 if luci.util.contains(self.keylist, key) then
1269 table.insert(self.keylist, tostring(key))
1270 table.insert(self.vallist, tostring(val))
1273 function MultiValue.valuelist(self, section)
1274 local val = self:cfgvalue(section)
1276 if not(type(val) == "string") then
1280 return luci.util.split(val, self.delimiter)
1283 function MultiValue.validate(self, val)
1284 val = (type(val) == "table") and val or {val}
1288 for i, value in ipairs(val) do
1289 if luci.util.contains(self.keylist, value) then
1290 result = result and (result .. self.delimiter .. value) or value
1298 StaticList = class(MultiValue)
1300 function StaticList.__init__(self, ...)
1301 MultiValue.__init__(self, ...)
1303 self.valuelist = self.cfgvalue
1305 if not self.override_scheme
1306 and self.map:get_scheme(self.section.sectiontype, self.option) then
1307 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1308 if self.value and vs.values and not self.override_values then
1309 for k, v in pairs(vs.values) do
1316 function StaticList.validate(self, value)
1317 value = (type(value) == "table") and value or {value}
1320 for i, v in ipairs(value) do
1321 if luci.util.contains(self.vallist, v) then
1322 table.insert(valid, v)
1329 DynamicList = class(AbstractValue)
1331 function DynamicList.__init__(self, ...)
1332 AbstractValue.__init__(self, ...)
1333 self.template = "cbi/dynlist"
1339 function DynamicList.value(self, key, val)
1341 table.insert(self.keylist, tostring(key))
1342 table.insert(self.vallist, tostring(val))
1345 function DynamicList.formvalue(self, section)
1346 local value = AbstractValue.formvalue(self, section)
1347 value = (type(value) == "table") and value or {value}
1350 for i, v in ipairs(value) do
1352 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1353 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1354 table.insert(valid, v)
1363 TextValue - A multi-line value
1366 TextValue = class(AbstractValue)
1368 function TextValue.__init__(self, ...)
1369 AbstractValue.__init__(self, ...)
1370 self.template = "cbi/tvalue"
1376 Button = class(AbstractValue)
1378 function Button.__init__(self, ...)
1379 AbstractValue.__init__(self, ...)
1380 self.template = "cbi/button"
1381 self.inputstyle = nil
1386 FileUpload = class(AbstractValue)
1388 function FileUpload.__init__(self, ...)
1389 AbstractValue.__init__(self, ...)
1390 self.template = "cbi/upload"
1391 if not self.map.upload_fields then
1392 self.map.upload_fields = { self }
1394 self.map.upload_fields[#self.map.upload_fields+1] = self
1398 function FileUpload.cfgvalue(self, section)
1399 local val = AbstractValue.cfgvalue(self, section)
1400 if val and luci.fs.access(val) then
1406 function FileUpload.formvalue(self, section)
1407 local val = AbstractValue.formvalue(self, section)
1409 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1410 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1420 function FileUpload.remove(self, section)
1421 local val = AbstractValue.formvalue(self, section)
1422 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1423 return AbstractValue.remove(self, section)