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")
34 local uci = require("luci.model.uci")
35 local class = luci.util.class
36 local instanceof = luci.util.instanceof
44 CREATE_PREFIX = "cbi.cts."
45 REMOVE_PREFIX = "cbi.rts."
47 -- Loads a CBI map from given file, creating an environment and returns it
48 function load(cbimap, ...)
50 local i18n = require "luci.i18n"
51 require("luci.config")
54 local cbidir = luci.util.libpath() .. "/model/cbi/"
55 local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
58 luci.i18n.loadc("cbi")
59 luci.i18n.loadc("uvl")
62 translate=i18n.translate,
63 translatef=i18n.translatef,
67 setfenv(func, setmetatable(env, {__index =
69 return rawget(tbl, key) or _M[key] or _G[key]
74 for i, map in ipairs(maps) do
75 if not instanceof(map, Node) then
76 error("CBI map returns no valid map object!")
84 local function _uvl_validate_section(node, name)
85 local co = node.map:get()
87 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
88 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
90 local function tag_fields(e)
91 if e.option and node.fields[e.option] then
92 node.fields[e.option].error = e
94 for _, c in ipairs(e.childs) do tag_fields(c) end
98 local function tag_section(e)
100 for _, c in ipairs(e.childs) do
101 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
102 table.insert( s, c.childs[1]:string() )
104 table.insert( s, c:string() )
107 if #s > 0 then node.error = s end
110 local stat, err = node.map.validator:validate_section(node.config, name, co)
112 node.map.save = false
117 if err:is(luci.uvl.errors.ERR_DEPENDENCY) then
118 node.tag_deperror[name] = err:string()
120 node.tag_invalid[name] = err:string()
122 for i, v in ipairs(err.childs) do
123 if v.option and node.fields[v.option] then
124 if v:is(luci.uvl.errors.ERR_DEPENDENCY) then
125 node.fields[v.option].tag_deperror[name] = v:string()
126 elseif v:is(luci.uvl.errors.ERR_OPT_REQUIRED) then
127 node.fields[v.option].tag_missing[name] = v:string()
128 node.tag_reqerror[name] = v:string()
129 elseif v:is(luci.uvl.errors.ERR_OPTION) then
130 node.fields[v.option].tag_invalid[name] = v:string()
139 local function _uvl_strip_remote_dependencies(deps)
142 for k, v in pairs(deps) do
143 k = k:gsub("%$config%.%$section%.", "")
144 if k:match("^[%w_]+$") and type(v) == "string" then
153 -- Node pseudo abstract class
156 function Node.__init__(self, title, description)
158 self.title = title or ""
159 self.description = description or ""
160 self.template = "cbi/node"
164 function Node._i18n(self, config, section, option, title, description)
167 if type(luci.i18n) == "table" then
169 local key = config and config:gsub("[^%w]+", "") or ""
171 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
172 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
174 self.title = title or luci.i18n.translate( key, option or section or config )
175 self.description = description or luci.i18n.translate( key .. "_desc", "" )
179 -- Append child nodes
180 function Node.append(self, obj)
181 table.insert(self.children, obj)
184 -- Parse this node and its children
185 function Node.parse(self, ...)
186 for k, child in ipairs(self.children) do
192 function Node.render(self, scope)
196 luci.template.render(self.template, scope)
199 -- Render the children
200 function Node.render_children(self, ...)
201 for k, node in ipairs(self.children) do
208 A simple template element
210 Template = class(Node)
212 function Template.__init__(self, template)
214 self.template = template
217 function Template.render(self)
218 luci.template.render(self.template, {self=self})
223 Map - A map describing a configuration file
227 function Map.__init__(self, config, ...)
228 Node.__init__(self, ...)
229 Node._i18n(self, config, nil, nil, ...)
232 self.parsechain = {self.config}
233 self.template = "cbi/map"
234 self.uci = uci.cursor()
236 if not self.uci:load(self.config) then
237 error("Unable to read UCI data: " .. self.config)
240 self.validator = luci.uvl.UVL()
241 self.scheme = self.validator:get_scheme(self.config)
245 function Map.get_scheme(self, sectiontype, option)
247 return self.scheme and self.scheme.sections[sectiontype]
249 return self.scheme and self.scheme.variables[sectiontype]
250 and self.scheme.variables[sectiontype][option]
255 -- Chain foreign config
256 function Map.chain(self, config)
257 table.insert(self.parsechain, config)
260 -- Use optimized UCI writing
261 function Map.parse(self, ...)
262 Node.parse(self, ...)
265 for i, config in ipairs(self.parsechain) do
266 self.uci:save(config)
268 if luci.http.formvalue("cbi.apply") then
269 for i, config in ipairs(self.parsechain) do
270 self.uci:commit(config)
271 self.uci:apply(config)
273 -- Refresh data because commit changes section names
274 self.uci:load(config)
278 Node.parse(self, ...)
281 for i, config in ipairs(self.parsechain) do
282 self.uci:unload(config)
287 -- Creates a child section
288 function Map.section(self, class, ...)
289 if instanceof(class, AbstractSection) then
290 local obj = class(self, ...)
294 error("class must be a descendent of AbstractSection")
299 function Map.add(self, sectiontype)
300 return self.uci:add(self.config, sectiontype)
304 function Map.set(self, section, option, value)
306 return self.uci:set(self.config, section, option, value)
308 return self.uci:set(self.config, section, value)
313 function Map.del(self, section, option)
315 return self.uci:delete(self.config, section, option)
317 return self.uci:delete(self.config, section)
322 function Map.get(self, section, option)
324 return self.uci:get_all(self.config)
326 return self.uci:get(self.config, section, option)
328 return self.uci:get_all(self.config, section)
338 Page.__init__ = Node.__init__
339 Page.parse = function() end
343 SimpleForm - A Simple non-UCI form
345 SimpleForm = class(Node)
347 function SimpleForm.__init__(self, config, title, description, data)
348 Node.__init__(self, title, description)
350 self.data = data or {}
351 self.template = "cbi/simpleform"
355 function SimpleForm.parse(self, ...)
356 if luci.http.formvalue("cbi.submit") then
357 Node.parse(self, 1, ...)
361 for k, j in ipairs(self.children) do
362 for i, v in ipairs(j.children) do
364 and (not v.tag_missing or not v.tag_missing[1])
365 and (not v.tag_invalid or not v.tag_invalid[1])
370 not luci.http.formvalue("cbi.submit") and 0
374 self.dorender = not self.handle or self:handle(state, self.data) ~= false
377 function SimpleForm.render(self, ...)
378 if self.dorender then
379 Node.render(self, ...)
383 function SimpleForm.section(self, class, ...)
384 if instanceof(class, AbstractSection) then
385 local obj = class(self, ...)
389 error("class must be a descendent of AbstractSection")
393 -- Creates a child field
394 function SimpleForm.field(self, class, ...)
396 for k, v in ipairs(self.children) do
397 if instanceof(v, SimpleSection) then
403 section = self:section(SimpleSection)
406 if instanceof(class, AbstractValue) then
407 local obj = class(self, section, ...)
408 obj.track_missing = true
412 error("class must be a descendent of AbstractValue")
416 function SimpleForm.set(self, section, option, value)
417 self.data[option] = value
421 function SimpleForm.del(self, section, option)
422 self.data[option] = nil
426 function SimpleForm.get(self, section, option)
427 return self.data[option]
431 function SimpleForm.get_scheme()
440 AbstractSection = class(Node)
442 function AbstractSection.__init__(self, map, sectiontype, ...)
443 Node.__init__(self, ...)
444 self.sectiontype = sectiontype
446 self.config = map.config
451 self.tag_invalid = {}
452 self.tag_deperror = {}
455 self.addremove = false
459 -- Appends a new option
460 function AbstractSection.option(self, class, option, ...)
461 -- Autodetect from UVL
462 if class == true and self.map:get_scheme(self.sectiontype, option) then
463 local vs = self.map:get_scheme(self.sectiontype, option)
464 if vs.type == "boolean" then
466 elseif vs.type == "list" then
468 elseif vs.type == "enum" or vs.type == "reference" then
475 if instanceof(class, AbstractValue) then
476 local obj = class(self.map, self, option, ...)
478 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
481 self.fields[option] = obj
483 elseif class == true then
484 error("No valid class was given and autodetection failed.")
486 error("class must be a descendant of AbstractValue")
490 -- Parse optional options
491 function AbstractSection.parse_optionals(self, section)
492 if not self.optional then
496 self.optionals[section] = {}
498 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
499 for k,v in ipairs(self.children) do
500 if v.optional and not v:cfgvalue(section) then
501 if field == v.option then
504 table.insert(self.optionals[section], v)
509 if field and #field > 0 and self.dynamic then
510 self:add_dynamic(field)
514 -- Add a dynamic option
515 function AbstractSection.add_dynamic(self, field, optional)
516 local o = self:option(Value, field, field)
517 o.optional = optional
520 -- Parse all dynamic options
521 function AbstractSection.parse_dynamic(self, section)
522 if not self.dynamic then
526 local arr = luci.util.clone(self:cfgvalue(section))
527 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
528 for k, v in pairs(form) do
532 for key,val in pairs(arr) do
535 for i,c in ipairs(self.children) do
536 if c.option == key then
541 if create and key:sub(1, 1) ~= "." then
542 self:add_dynamic(key, true)
547 -- Returns the section's UCI table
548 function AbstractSection.cfgvalue(self, section)
549 return self.map:get(section)
552 -- Removes the section
553 function AbstractSection.remove(self, section)
554 return self.map:del(section)
557 -- Creates the section
558 function AbstractSection.create(self, section)
562 stat = self.map:set(section, nil, self.sectiontype)
564 section = self.map:add(self.sectiontype)
569 for k,v in pairs(self.children) do
571 self.map:set(section, v.option, v.default)
575 for k,v in pairs(self.defaults) do
576 self.map:set(section, k, v)
584 SimpleSection = class(AbstractSection)
586 function SimpleSection.__init__(self, form, ...)
587 AbstractSection.__init__(self, form, nil, ...)
588 self.template = "cbi/nullsection"
592 Table = class(AbstractSection)
594 function Table.__init__(self, form, data, ...)
595 local datasource = {}
596 datasource.config = "table"
599 function datasource.get(self, section, option)
600 return data[section] and data[section][option]
603 function datasource.del(...)
607 function datasource.get_scheme()
611 AbstractSection.__init__(self, datasource, "table", ...)
612 self.template = "cbi/tblsection"
613 self.rowcolors = true
614 self.anonymous = true
617 function Table.parse(self)
618 for i, k in ipairs(self:cfgsections()) do
619 if luci.http.formvalue("cbi.submit") then
625 function Table.cfgsections(self)
628 for i, v in luci.util.kspairs(self.data) do
629 table.insert(sections, i)
638 NamedSection - A fixed configuration section defined by its name
640 NamedSection = class(AbstractSection)
642 function NamedSection.__init__(self, map, section, stype, ...)
643 AbstractSection.__init__(self, map, stype, ...)
644 Node._i18n(self, map.config, section, nil, ...)
647 self.addremove = false
649 -- Use defaults from UVL
650 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
651 local vs = self.map:get_scheme(self.sectiontype)
652 self.addremove = not vs.unique and not vs.required
653 self.dynamic = vs.dynamic
654 self.title = self.title or vs.title
655 self.description = self.description or vs.descr
658 self.template = "cbi/nsection"
659 self.section = section
662 function NamedSection.parse(self)
663 local s = self.section
664 local active = self:cfgvalue(s)
666 if self.addremove then
667 local path = self.config.."."..s
668 if active then -- Remove the section
669 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
672 else -- Create and apply default values
673 if luci.http.formvalue("cbi.cns."..path) then
681 AbstractSection.parse_dynamic(self, s)
682 if luci.http.formvalue("cbi.submit") then
685 if not self.override_scheme and self.map.scheme then
686 _uvl_validate_section(self, s)
689 AbstractSection.parse_optionals(self, s)
695 TypedSection - A (set of) configuration section(s) defined by the type
696 addremove: Defines whether the user can add/remove sections of this type
697 anonymous: Allow creating anonymous sections
698 validate: a validation function returning nil if the section is invalid
700 TypedSection = class(AbstractSection)
702 function TypedSection.__init__(self, map, type, ...)
703 AbstractSection.__init__(self, map, type, ...)
704 Node._i18n(self, map.config, type, nil, ...)
706 self.template = "cbi/tsection"
708 self.anonymous = false
710 -- Use defaults from UVL
711 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
712 local vs = self.map:get_scheme(self.sectiontype)
713 self.addremove = not vs.unique and not vs.required
714 self.dynamic = vs.dynamic
715 self.anonymous = not vs.named
716 self.title = self.title or vs.title
717 self.description = self.description or vs.descr
721 -- Return all matching UCI sections for this TypedSection
722 function TypedSection.cfgsections(self)
724 self.map.uci:foreach(self.map.config, self.sectiontype,
726 if self:checkscope(section[".name"]) then
727 table.insert(sections, section[".name"])
734 -- Limits scope to sections that have certain option => value pairs
735 function TypedSection.depends(self, option, value)
736 table.insert(self.deps, {option=option, value=value})
739 function TypedSection.parse(self)
740 if self.addremove then
742 local crval = REMOVE_PREFIX .. self.config
743 local name = luci.http.formvaluetable(crval)
744 for k,v in pairs(name) do
745 if k:sub(-2) == ".x" then
748 if self:cfgvalue(k) and self:checkscope(k) then
755 for i, k in ipairs(self:cfgsections()) do
756 AbstractSection.parse_dynamic(self, k)
757 if luci.http.formvalue("cbi.submit") then
760 if not self.override_scheme and self.map.scheme then
761 _uvl_validate_section(self, k)
764 AbstractSection.parse_optionals(self, k)
767 if self.addremove then
770 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
771 local name = luci.http.formvalue(crval)
772 if self.anonymous then
774 created = self:create()
778 -- Ignore if it already exists
779 if self:cfgvalue(name) then
783 name = self:checkscope(name)
786 self.err_invalid = true
789 if name and #name > 0 then
790 created = self:create(name) and name
796 AbstractSection.parse_optionals(self, created)
801 -- Verifies scope of sections
802 function TypedSection.checkscope(self, section)
803 -- Check if we are not excluded
804 if self.filter and not self:filter(section) then
808 -- Check if at least one dependency is met
809 if #self.deps > 0 and self:cfgvalue(section) then
812 for k, v in ipairs(self.deps) do
813 if self:cfgvalue(section)[v.option] == v.value then
823 return self:validate(section)
827 -- Dummy validate function
828 function TypedSection.validate(self, section)
834 AbstractValue - An abstract Value Type
835 null: Value can be empty
836 valid: A function returning the value if it is valid otherwise nil
837 depends: A table of option => value pairs of which one must be true
838 default: The default value
839 size: The size of the input fields
840 rmempty: Unset value if empty
841 optional: This value is optional (see AbstractSection.optionals)
843 AbstractValue = class(Node)
845 function AbstractValue.__init__(self, map, section, option, ...)
846 Node.__init__(self, ...)
847 self.section = section
850 self.config = map.config
851 self.tag_invalid = {}
852 self.tag_missing = {}
853 self.tag_reqerror = {}
858 self.track_missing = false
862 self.optional = false
864 -- Use defaults from UVL
865 if not self.override_scheme
866 and self.map:get_scheme(self.section.sectiontype, self.option) then
867 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
868 self.rmempty = not vs.required
869 self.cast = (vs.type == "list") and "list" or "string"
870 self.title = self.title or vs.title
871 self.description = self.description or vs.descr
872 self.default = vs.default
874 if vs.depends and not self.override_dependencies then
875 for i, deps in ipairs(vs.depends) do
876 deps = _uvl_strip_remote_dependencies(deps)
885 -- Add a dependencie to another section field
886 function AbstractValue.depends(self, field, value)
888 if type(field) == "string" then
895 table.insert(self.deps, {deps=deps, add=""})
898 -- Generates the unique CBID
899 function AbstractValue.cbid(self, section)
900 return "cbid."..self.map.config.."."..section.."."..self.option
903 -- Return whether this object should be created
904 function AbstractValue.formcreated(self, section)
905 local key = "cbi.opt."..self.config.."."..section
906 return (luci.http.formvalue(key) == self.option)
909 -- Returns the formvalue for this object
910 function AbstractValue.formvalue(self, section)
911 return luci.http.formvalue(self:cbid(section))
914 function AbstractValue.additional(self, value)
915 self.optional = value
918 function AbstractValue.mandatory(self, value)
919 self.rmempty = not value
922 function AbstractValue.parse(self, section)
923 local fvalue = self:formvalue(section)
924 local cvalue = self:cfgvalue(section)
926 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
927 fvalue = self:transform(self:validate(fvalue, section))
929 self.tag_invalid[section] = true
931 if fvalue and not (fvalue == cvalue) then
932 self:write(section, fvalue)
934 else -- Unset the UCI or error
935 if self.rmempty or self.optional then
937 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
938 self.tag_missing[section] = true
943 -- Render if this value exists or if it is mandatory
944 function AbstractValue.render(self, s, scope)
945 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
948 scope.cbid = self:cbid(s)
949 scope.striptags = luci.util.striptags
951 scope.ifattr = function(cond,key,val)
953 return string.format(
954 ' %s="%s"', tostring(key),
955 luci.util.pcdata(tostring( val
957 or (type(self[key]) ~= "function" and self[key])
965 scope.attr = function(...)
966 return scope.ifattr( true, ... )
969 Node.render(self, scope)
973 -- Return the UCI value of this object
974 function AbstractValue.cfgvalue(self, section)
975 local value = self.map:get(section, self.option)
976 if not self.cast or self.cast == type(value) then
978 elseif self.cast == "string" then
979 if type(value) == "table" then
982 elseif self.cast == "table" then
987 -- Validate the form value
988 function AbstractValue.validate(self, value)
992 AbstractValue.transform = AbstractValue.validate
996 function AbstractValue.write(self, section, value)
997 return self.map:set(section, self.option, value)
1001 function AbstractValue.remove(self, section)
1002 return self.map:del(section, self.option)
1009 Value - A one-line value
1010 maxlength: The maximum length
1012 Value = class(AbstractValue)
1014 function Value.__init__(self, ...)
1015 AbstractValue.__init__(self, ...)
1016 self.template = "cbi/value"
1021 function Value.value(self, key, val)
1023 table.insert(self.keylist, tostring(key))
1024 table.insert(self.vallist, tostring(val))
1028 -- DummyValue - This does nothing except being there
1029 DummyValue = class(AbstractValue)
1031 function DummyValue.__init__(self, ...)
1032 AbstractValue.__init__(self, ...)
1033 self.template = "cbi/dvalue"
1037 function DummyValue.parse(self)
1043 Flag - A flag being enabled or disabled
1045 Flag = class(AbstractValue)
1047 function Flag.__init__(self, ...)
1048 AbstractValue.__init__(self, ...)
1049 self.template = "cbi/fvalue"
1055 -- A flag can only have two states: set or unset
1056 function Flag.parse(self, section)
1057 local fvalue = self:formvalue(section)
1060 fvalue = self.enabled
1062 fvalue = self.disabled
1065 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1066 if not(fvalue == self:cfgvalue(section)) then
1067 self:write(section, fvalue)
1070 self:remove(section)
1077 ListValue - A one-line value predefined in a list
1078 widget: The widget that will be used (select, radio)
1080 ListValue = class(AbstractValue)
1082 function ListValue.__init__(self, ...)
1083 AbstractValue.__init__(self, ...)
1084 self.template = "cbi/lvalue"
1089 self.widget = "select"
1091 if not self.override_scheme
1092 and self.map:get_scheme(self.section.sectiontype, self.option) then
1093 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1094 if self.value and vs.values and not self.override_values then
1095 if self.rmempty or self.optional then
1098 for k, v in pairs(vs.values) do
1100 if not self.override_dependencies
1101 and vs.enum_depends and vs.enum_depends[k] then
1102 for i, dep in ipairs(vs.enum_depends[k]) do
1103 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1106 self:value(k, v, unpack(deps))
1112 function ListValue.value(self, key, val, ...)
1113 if luci.util.contains(self.keylist, key) then
1118 table.insert(self.keylist, tostring(key))
1119 table.insert(self.vallist, tostring(val))
1121 for i, deps in ipairs({...}) do
1122 table.insert(self.deps, {add = "-"..key, deps=deps})
1126 function ListValue.validate(self, val)
1127 if luci.util.contains(self.keylist, val) then
1137 MultiValue - Multiple delimited values
1138 widget: The widget that will be used (select, checkbox)
1139 delimiter: The delimiter that will separate the values (default: " ")
1141 MultiValue = class(AbstractValue)
1143 function MultiValue.__init__(self, ...)
1144 AbstractValue.__init__(self, ...)
1145 self.template = "cbi/mvalue"
1150 self.widget = "checkbox"
1151 self.delimiter = " "
1154 function MultiValue.render(self, ...)
1155 if self.widget == "select" and not self.size then
1156 self.size = #self.vallist
1159 AbstractValue.render(self, ...)
1162 function MultiValue.value(self, key, val)
1163 if luci.util.contains(self.keylist, key) then
1168 table.insert(self.keylist, tostring(key))
1169 table.insert(self.vallist, tostring(val))
1172 function MultiValue.valuelist(self, section)
1173 local val = self:cfgvalue(section)
1175 if not(type(val) == "string") then
1179 return luci.util.split(val, self.delimiter)
1182 function MultiValue.validate(self, val)
1183 val = (type(val) == "table") and val or {val}
1187 for i, value in ipairs(val) do
1188 if luci.util.contains(self.keylist, value) then
1189 result = result and (result .. self.delimiter .. value) or value
1197 StaticList = class(MultiValue)
1199 function StaticList.__init__(self, ...)
1200 MultiValue.__init__(self, ...)
1202 self.valuelist = self.cfgvalue
1204 if not self.override_scheme
1205 and self.map:get_scheme(self.section.sectiontype, self.option) then
1206 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1207 if self.value and vs.values and not self.override_values then
1208 for k, v in pairs(vs.values) do
1215 function StaticList.validate(self, value)
1216 value = (type(value) == "table") and value or {value}
1219 for i, v in ipairs(value) do
1220 if luci.util.contains(self.valuelist, v) then
1221 table.insert(valid, v)
1228 DynamicList = class(AbstractValue)
1230 function DynamicList.__init__(self, ...)
1231 AbstractValue.__init__(self, ...)
1232 self.template = "cbi/dynlist"
1238 function DynamicList.value(self, key, val)
1240 table.insert(self.keylist, tostring(key))
1241 table.insert(self.vallist, tostring(val))
1244 function DynamicList.validate(self, value, section)
1245 value = (type(value) == "table") and value or {value}
1248 for i, v in ipairs(value) do
1250 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1251 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1252 table.insert(valid, v)
1261 TextValue - A multi-line value
1264 TextValue = class(AbstractValue)
1266 function TextValue.__init__(self, ...)
1267 AbstractValue.__init__(self, ...)
1268 self.template = "cbi/tvalue"
1274 Button = class(AbstractValue)
1276 function Button.__init__(self, ...)
1277 AbstractValue.__init__(self, ...)
1278 self.template = "cbi/button"
1279 self.inputstyle = nil