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 return self.changed and FORM_CHANGED or FORM_VALID
364 function Map.render(self, ...)
365 Node.render(self, ...)
367 local fp = self._apply()
373 -- Creates a child section
374 function Map.section(self, class, ...)
375 if instanceof(class, AbstractSection) then
376 local obj = class(self, ...)
380 error("class must be a descendent of AbstractSection")
385 function Map.add(self, sectiontype)
386 return self.uci:add(self.config, sectiontype)
390 function Map.set(self, section, option, value)
392 return self.uci:set(self.config, section, option, value)
394 return self.uci:set(self.config, section, value)
399 function Map.del(self, section, option)
401 return self.uci:delete(self.config, section, option)
403 return self.uci:delete(self.config, section)
408 function Map.get(self, section, option)
410 return self.uci:get_all(self.config)
412 return self.uci:get(self.config, section, option)
414 return self.uci:get_all(self.config, section)
424 Page.__init__ = Node.__init__
425 Page.parse = function() end
429 SimpleForm - A Simple non-UCI form
431 SimpleForm = class(Node)
433 function SimpleForm.__init__(self, config, title, description, data)
434 Node.__init__(self, title, description)
436 self.data = data or {}
437 self.template = "cbi/simpleform"
439 self.pageaction = false
442 function SimpleForm.parse(self, ...)
443 if luci.http.formvalue("cbi.submit") then
444 Node.parse(self, 1, ...)
448 for k, j in ipairs(self.children) do
449 for i, v in ipairs(j.children) do
451 and (not v.tag_missing or not v.tag_missing[1])
452 and (not v.tag_invalid or not v.tag_invalid[1])
457 not self:submitstate() and FORM_NODATA
458 or valid and FORM_VALID
461 self.dorender = not self.handle or self:handle(state, self.data) ~= false
465 function SimpleForm.render(self, ...)
466 if self.dorender then
467 Node.render(self, ...)
471 function SimpleForm.submitstate(self)
472 return luci.http.formvalue("cbi.submit")
475 function SimpleForm.section(self, class, ...)
476 if instanceof(class, AbstractSection) then
477 local obj = class(self, ...)
481 error("class must be a descendent of AbstractSection")
485 -- Creates a child field
486 function SimpleForm.field(self, class, ...)
488 for k, v in ipairs(self.children) do
489 if instanceof(v, SimpleSection) then
495 section = self:section(SimpleSection)
498 if instanceof(class, AbstractValue) then
499 local obj = class(self, section, ...)
500 obj.track_missing = true
504 error("class must be a descendent of AbstractValue")
508 function SimpleForm.set(self, section, option, value)
509 self.data[option] = value
513 function SimpleForm.del(self, section, option)
514 self.data[option] = nil
518 function SimpleForm.get(self, section, option)
519 return self.data[option]
523 function SimpleForm.get_scheme()
532 AbstractSection = class(Node)
534 function AbstractSection.__init__(self, map, sectiontype, ...)
535 Node.__init__(self, ...)
536 self.sectiontype = sectiontype
538 self.config = map.config
543 self.tag_invalid = {}
544 self.tag_deperror = {}
547 self.addremove = false
551 -- Appends a new option
552 function AbstractSection.option(self, class, option, ...)
553 -- Autodetect from UVL
554 if class == true and self.map:get_scheme(self.sectiontype, option) then
555 local vs = self.map:get_scheme(self.sectiontype, option)
556 if vs.type == "boolean" then
558 elseif vs.type == "list" then
560 elseif vs.type == "enum" or vs.type == "reference" then
567 if instanceof(class, AbstractValue) then
568 local obj = class(self.map, self, option, ...)
570 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
573 self.fields[option] = obj
575 elseif class == true then
576 error("No valid class was given and autodetection failed.")
578 error("class must be a descendant of AbstractValue")
582 -- Parse optional options
583 function AbstractSection.parse_optionals(self, section)
584 if not self.optional then
588 self.optionals[section] = {}
590 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
591 for k,v in ipairs(self.children) do
592 if v.optional and not v:cfgvalue(section) then
593 if field == v.option then
596 table.insert(self.optionals[section], v)
601 if field and #field > 0 and self.dynamic then
602 self:add_dynamic(field)
606 -- Add a dynamic option
607 function AbstractSection.add_dynamic(self, field, optional)
608 local o = self:option(Value, field, field)
609 o.optional = optional
612 -- Parse all dynamic options
613 function AbstractSection.parse_dynamic(self, section)
614 if not self.dynamic then
618 local arr = luci.util.clone(self:cfgvalue(section))
619 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
620 for k, v in pairs(form) do
624 for key,val in pairs(arr) do
627 for i,c in ipairs(self.children) do
628 if c.option == key then
633 if create and key:sub(1, 1) ~= "." then
634 self:add_dynamic(key, true)
639 -- Returns the section's UCI table
640 function AbstractSection.cfgvalue(self, section)
641 return self.map:get(section)
644 -- Removes the section
645 function AbstractSection.remove(self, section)
646 return self.map:del(section)
649 -- Creates the section
650 function AbstractSection.create(self, section)
654 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
656 section = self.map:add(self.sectiontype)
661 for k,v in pairs(self.children) do
663 self.map:set(section, v.option, v.default)
667 for k,v in pairs(self.defaults) do
668 self.map:set(section, k, v)
676 SimpleSection = class(AbstractSection)
678 function SimpleSection.__init__(self, form, ...)
679 AbstractSection.__init__(self, form, nil, ...)
680 self.template = "cbi/nullsection"
684 Table = class(AbstractSection)
686 function Table.__init__(self, form, data, ...)
687 local datasource = {}
688 datasource.config = "table"
691 function datasource.get(self, section, option)
692 return data[section] and data[section][option]
695 function datasource.submitstate(self)
696 return luci.http.formvalue("cbi.submit")
699 function datasource.del(...)
703 function datasource.get_scheme()
707 AbstractSection.__init__(self, datasource, "table", ...)
708 self.template = "cbi/tblsection"
709 self.rowcolors = true
710 self.anonymous = true
713 function Table.parse(self)
714 for i, k in ipairs(self:cfgsections()) do
715 if self.map:submitstate() then
721 function Table.cfgsections(self)
724 for i, v in luci.util.kspairs(self.data) do
725 table.insert(sections, i)
734 NamedSection - A fixed configuration section defined by its name
736 NamedSection = class(AbstractSection)
738 function NamedSection.__init__(self, map, section, stype, ...)
739 AbstractSection.__init__(self, map, stype, ...)
740 Node._i18n(self, map.config, section, nil, ...)
743 self.addremove = false
745 -- Use defaults from UVL
746 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
747 local vs = self.map:get_scheme(self.sectiontype)
748 self.addremove = not vs.unique and not vs.required
749 self.dynamic = vs.dynamic
750 self.title = self.title or vs.title
751 self.description = self.description or vs.descr
754 self.template = "cbi/nsection"
755 self.section = section
758 function NamedSection.parse(self, novld)
759 local s = self.section
760 local active = self:cfgvalue(s)
762 if self.addremove then
763 local path = self.config.."."..s
764 if active then -- Remove the section
765 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
768 else -- Create and apply default values
769 if luci.http.formvalue("cbi.cns."..path) then
777 AbstractSection.parse_dynamic(self, s)
778 if self.map:submitstate() then
781 if not novld and not self.override_scheme and self.map.scheme then
782 _uvl_validate_section(self, s)
785 AbstractSection.parse_optionals(self, s)
791 TypedSection - A (set of) configuration section(s) defined by the type
792 addremove: Defines whether the user can add/remove sections of this type
793 anonymous: Allow creating anonymous sections
794 validate: a validation function returning nil if the section is invalid
796 TypedSection = class(AbstractSection)
798 function TypedSection.__init__(self, map, type, ...)
799 AbstractSection.__init__(self, map, type, ...)
800 Node._i18n(self, map.config, type, nil, ...)
802 self.template = "cbi/tsection"
804 self.anonymous = false
806 -- Use defaults from UVL
807 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
808 local vs = self.map:get_scheme(self.sectiontype)
809 self.addremove = not vs.unique and not vs.required
810 self.dynamic = vs.dynamic
811 self.anonymous = not vs.named
812 self.title = self.title or vs.title
813 self.description = self.description or vs.descr
817 -- Return all matching UCI sections for this TypedSection
818 function TypedSection.cfgsections(self)
820 self.map.uci:foreach(self.map.config, self.sectiontype,
822 if self:checkscope(section[".name"]) then
823 table.insert(sections, section[".name"])
830 -- Limits scope to sections that have certain option => value pairs
831 function TypedSection.depends(self, option, value)
832 table.insert(self.deps, {option=option, value=value})
835 function TypedSection.parse(self, novld)
836 if self.addremove then
838 local crval = REMOVE_PREFIX .. self.config
839 local name = luci.http.formvaluetable(crval)
840 for k,v in pairs(name) do
841 if k:sub(-2) == ".x" then
844 if self:cfgvalue(k) and self:checkscope(k) then
851 for i, k in ipairs(self:cfgsections()) do
852 AbstractSection.parse_dynamic(self, k)
853 if self.map:submitstate() then
856 if not novld and not self.override_scheme and self.map.scheme then
857 _uvl_validate_section(self, k)
860 AbstractSection.parse_optionals(self, k)
863 if self.addremove then
866 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
867 local name = luci.http.formvalue(crval)
868 if self.anonymous then
870 created = self:create()
874 -- Ignore if it already exists
875 if self:cfgvalue(name) then
879 name = self:checkscope(name)
882 self.err_invalid = true
885 if name and #name > 0 then
886 created = self:create(name) and name
888 self.invalid_cts = true
895 AbstractSection.parse_optionals(self, created)
900 -- Verifies scope of sections
901 function TypedSection.checkscope(self, section)
902 -- Check if we are not excluded
903 if self.filter and not self:filter(section) then
907 -- Check if at least one dependency is met
908 if #self.deps > 0 and self:cfgvalue(section) then
911 for k, v in ipairs(self.deps) do
912 if self:cfgvalue(section)[v.option] == v.value then
922 return self:validate(section)
926 -- Dummy validate function
927 function TypedSection.validate(self, section)
933 AbstractValue - An abstract Value Type
934 null: Value can be empty
935 valid: A function returning the value if it is valid otherwise nil
936 depends: A table of option => value pairs of which one must be true
937 default: The default value
938 size: The size of the input fields
939 rmempty: Unset value if empty
940 optional: This value is optional (see AbstractSection.optionals)
942 AbstractValue = class(Node)
944 function AbstractValue.__init__(self, map, section, option, ...)
945 Node.__init__(self, ...)
946 self.section = section
949 self.config = map.config
950 self.tag_invalid = {}
951 self.tag_missing = {}
952 self.tag_reqerror = {}
955 --self.cast = "string"
957 self.track_missing = false
958 --self.rmempty = false
961 self.optional = false
964 function AbstractValue.prepare(self)
965 -- Use defaults from UVL
966 if not self.override_scheme
967 and self.map:get_scheme(self.section.sectiontype, self.option) then
968 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
969 if self.rmempty == nil then
970 self.rmempty = not vs.required
972 if self.cast == nil then
973 self.cast = (vs.type == "list") and "list" or "string"
975 self.title = self.title or vs.title
976 self.description = self.description or vs.descr
977 if self.default == nil then
978 self.default = vs.default
981 if vs.depends and not self.override_dependencies then
982 for i, deps in ipairs(vs.depends) do
983 deps = _uvl_strip_remote_dependencies(deps)
991 self.cast = self.cast or "string"
994 -- Add a dependencie to another section field
995 function AbstractValue.depends(self, field, value)
997 if type(field) == "string" then
1004 table.insert(self.deps, {deps=deps, add=""})
1007 -- Generates the unique CBID
1008 function AbstractValue.cbid(self, section)
1009 return "cbid."..self.map.config.."."..section.."."..self.option
1012 -- Return whether this object should be created
1013 function AbstractValue.formcreated(self, section)
1014 local key = "cbi.opt."..self.config.."."..section
1015 return (luci.http.formvalue(key) == self.option)
1018 -- Returns the formvalue for this object
1019 function AbstractValue.formvalue(self, section)
1020 return luci.http.formvalue(self:cbid(section))
1023 function AbstractValue.additional(self, value)
1024 self.optional = value
1027 function AbstractValue.mandatory(self, value)
1028 self.rmempty = not value
1031 function AbstractValue.parse(self, section)
1032 local fvalue = self:formvalue(section)
1033 local cvalue = self:cfgvalue(section)
1035 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1036 fvalue = self:transform(self:validate(fvalue, section))
1038 self.tag_invalid[section] = true
1040 if fvalue and not (fvalue == cvalue) then
1041 self:write(section, fvalue)
1043 else -- Unset the UCI or error
1044 if self.rmempty or self.optional then
1045 self:remove(section)
1046 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1047 self.tag_missing[section] = true
1052 -- Render if this value exists or if it is mandatory
1053 function AbstractValue.render(self, s, scope)
1054 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1057 scope.cbid = self:cbid(s)
1058 scope.striptags = luci.util.striptags
1060 scope.ifattr = function(cond,key,val)
1062 return string.format(
1063 ' %s="%s"', tostring(key),
1064 luci.util.pcdata(tostring( val
1066 or (type(self[key]) ~= "function" and self[key])
1074 scope.attr = function(...)
1075 return scope.ifattr( true, ... )
1078 Node.render(self, scope)
1082 -- Return the UCI value of this object
1083 function AbstractValue.cfgvalue(self, section)
1084 local value = self.map:get(section, self.option)
1087 elseif not self.cast or self.cast == type(value) then
1089 elseif self.cast == "string" then
1090 if type(value) == "table" then
1093 elseif self.cast == "table" then
1094 return luci.util.split(value, "%s+", nil, true)
1098 -- Validate the form value
1099 function AbstractValue.validate(self, value)
1103 AbstractValue.transform = AbstractValue.validate
1107 function AbstractValue.write(self, section, value)
1108 return self.map:set(section, self.option, value)
1112 function AbstractValue.remove(self, section)
1113 return self.map:del(section, self.option)
1120 Value - A one-line value
1121 maxlength: The maximum length
1123 Value = class(AbstractValue)
1125 function Value.__init__(self, ...)
1126 AbstractValue.__init__(self, ...)
1127 self.template = "cbi/value"
1132 function Value.value(self, key, val)
1134 table.insert(self.keylist, tostring(key))
1135 table.insert(self.vallist, tostring(val))
1139 -- DummyValue - This does nothing except being there
1140 DummyValue = class(AbstractValue)
1142 function DummyValue.__init__(self, ...)
1143 AbstractValue.__init__(self, ...)
1144 self.template = "cbi/dvalue"
1148 function DummyValue.cfgvalue(self, section)
1151 if type(self.value) == "function" then
1152 value = self:value(section)
1157 value = AbstractValue.cfgvalue(self, section)
1162 function DummyValue.parse(self)
1168 Flag - A flag being enabled or disabled
1170 Flag = class(AbstractValue)
1172 function Flag.__init__(self, ...)
1173 AbstractValue.__init__(self, ...)
1174 self.template = "cbi/fvalue"
1180 -- A flag can only have two states: set or unset
1181 function Flag.parse(self, section)
1182 local fvalue = self:formvalue(section)
1185 fvalue = self.enabled
1187 fvalue = self.disabled
1190 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1191 if not(fvalue == self:cfgvalue(section)) then
1192 self:write(section, fvalue)
1195 self:remove(section)
1202 ListValue - A one-line value predefined in a list
1203 widget: The widget that will be used (select, radio)
1205 ListValue = class(AbstractValue)
1207 function ListValue.__init__(self, ...)
1208 AbstractValue.__init__(self, ...)
1209 self.template = "cbi/lvalue"
1214 self.widget = "select"
1217 function ListValue.prepare(self, ...)
1218 AbstractValue.prepare(self, ...)
1219 if not self.override_scheme
1220 and self.map:get_scheme(self.section.sectiontype, self.option) then
1221 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1222 if self.value and vs.valuelist and not self.override_values then
1223 for k, v in ipairs(vs.valuelist) do
1225 if not self.override_dependencies
1226 and vs.enum_depends and vs.enum_depends[v.value] then
1227 for i, dep in ipairs(vs.enum_depends[v.value]) do
1228 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1231 self:value(v.value, v.title or v.value, unpack(deps))
1237 function ListValue.value(self, key, val, ...)
1238 if luci.util.contains(self.keylist, key) then
1243 table.insert(self.keylist, tostring(key))
1244 table.insert(self.vallist, tostring(val))
1246 for i, deps in ipairs({...}) do
1247 table.insert(self.deps, {add = "-"..key, deps=deps})
1251 function ListValue.validate(self, val)
1252 if luci.util.contains(self.keylist, val) then
1262 MultiValue - Multiple delimited values
1263 widget: The widget that will be used (select, checkbox)
1264 delimiter: The delimiter that will separate the values (default: " ")
1266 MultiValue = class(AbstractValue)
1268 function MultiValue.__init__(self, ...)
1269 AbstractValue.__init__(self, ...)
1270 self.template = "cbi/mvalue"
1275 self.widget = "checkbox"
1276 self.delimiter = " "
1279 function MultiValue.render(self, ...)
1280 if self.widget == "select" and not self.size then
1281 self.size = #self.vallist
1284 AbstractValue.render(self, ...)
1287 function MultiValue.value(self, key, val)
1288 if luci.util.contains(self.keylist, key) then
1293 table.insert(self.keylist, tostring(key))
1294 table.insert(self.vallist, tostring(val))
1297 function MultiValue.valuelist(self, section)
1298 local val = self:cfgvalue(section)
1300 if not(type(val) == "string") then
1304 return luci.util.split(val, self.delimiter)
1307 function MultiValue.validate(self, val)
1308 val = (type(val) == "table") and val or {val}
1312 for i, value in ipairs(val) do
1313 if luci.util.contains(self.keylist, value) then
1314 result = result and (result .. self.delimiter .. value) or value
1322 StaticList = class(MultiValue)
1324 function StaticList.__init__(self, ...)
1325 MultiValue.__init__(self, ...)
1327 self.valuelist = self.cfgvalue
1329 if not self.override_scheme
1330 and self.map:get_scheme(self.section.sectiontype, self.option) then
1331 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1332 if self.value and vs.values and not self.override_values then
1333 for k, v in pairs(vs.values) do
1340 function StaticList.validate(self, value)
1341 value = (type(value) == "table") and value or {value}
1344 for i, v in ipairs(value) do
1345 if luci.util.contains(self.vallist, v) then
1346 table.insert(valid, v)
1353 DynamicList = class(AbstractValue)
1355 function DynamicList.__init__(self, ...)
1356 AbstractValue.__init__(self, ...)
1357 self.template = "cbi/dynlist"
1363 function DynamicList.value(self, key, val)
1365 table.insert(self.keylist, tostring(key))
1366 table.insert(self.vallist, tostring(val))
1369 function DynamicList.formvalue(self, section)
1370 local value = AbstractValue.formvalue(self, section)
1371 value = (type(value) == "table") and value or {value}
1374 for i, v in ipairs(value) do
1376 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1377 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1378 table.insert(valid, v)
1387 TextValue - A multi-line value
1390 TextValue = class(AbstractValue)
1392 function TextValue.__init__(self, ...)
1393 AbstractValue.__init__(self, ...)
1394 self.template = "cbi/tvalue"
1400 Button = class(AbstractValue)
1402 function Button.__init__(self, ...)
1403 AbstractValue.__init__(self, ...)
1404 self.template = "cbi/button"
1405 self.inputstyle = nil
1410 FileUpload = class(AbstractValue)
1412 function FileUpload.__init__(self, ...)
1413 AbstractValue.__init__(self, ...)
1414 self.template = "cbi/upload"
1415 if not self.map.upload_fields then
1416 self.map.upload_fields = { self }
1418 self.map.upload_fields[#self.map.upload_fields+1] = self
1422 function FileUpload.formcreated(self, section)
1423 return AbstractValue.formcreated(self, section) or
1424 luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
1425 luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1428 function FileUpload.cfgvalue(self, section)
1429 local val = AbstractValue.cfgvalue(self, section)
1430 if val and luci.fs.access(val) then
1436 function FileUpload.formvalue(self, section)
1437 local val = AbstractValue.formvalue(self, section)
1439 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1440 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1450 function FileUpload.remove(self, section)
1451 local val = AbstractValue.formvalue(self, section)
1452 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1453 return AbstractValue.remove(self, section)
1457 FileBrowser = class(AbstractValue)
1459 function FileBrowser.__init__(self, ...)
1460 AbstractValue.__init__(self, ...)
1461 self.template = "cbi/browser"