2 LuCI - Configuration Bind Interface
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
12 Copyright 2008 Steven Barth <steven@midlink.org>
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at
18 http://www.apache.org/licenses/LICENSE-2.0
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
27 module("luci.cbi", package.seeall)
29 require("luci.template")
30 local util = require("luci.util")
35 --local event = require "luci.sys.event"
36 local uci = require("luci.model.uci")
37 local class = util.class
38 local instanceof = util.instanceof
48 CREATE_PREFIX = "cbi.cts."
49 REMOVE_PREFIX = "cbi.rts."
51 -- Loads a CBI map from given file, creating an environment and returns it
52 function load(cbimap, ...)
54 local i18n = require "luci.i18n"
55 require("luci.config")
58 local upldir = "/lib/uci/upload/"
59 local cbidir = luci.util.libpath() .. "/model/cbi/"
61 assert(luci.fs.stat(cbimap) or luci.fs.stat(cbidir..cbimap..".lua"),
64 local func, err = loadfile(cbimap)
66 func, err = loadfile(cbidir..cbimap..".lua")
70 luci.i18n.loadc("cbi")
71 luci.i18n.loadc("uvl")
74 translate=i18n.translate,
75 translatef=i18n.translatef,
79 setfenv(func, setmetatable(env, {__index =
81 return rawget(tbl, key) or _M[key] or _G[key]
84 local maps = { func() }
86 local has_upload = false
88 for i, map in ipairs(maps) do
89 if not instanceof(map, Node) then
90 error("CBI map returns no valid map object!")
94 if map.upload_fields then
96 for _, field in ipairs(map.upload_fields) do
98 field.config .. '.' ..
99 field.section.sectiontype .. '.' ..
108 local uci = luci.model.uci.cursor()
109 local prm = luci.http.context.request.message.params
112 luci.http.setfilehandler(
113 function( field, chunk, eof )
114 if not field then return end
115 if field.name and not cbid then
116 local c, s, o = field.name:gmatch(
117 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
120 if c and s and o then
121 local t = uci:get( c, s )
122 if t and uploads[c.."."..t.."."..o] then
123 local path = upldir .. field.name
124 fd = io.open(path, "w")
133 if field.name == cbid and fd then
149 local function _uvl_validate_section(node, name)
150 local co = node.map:get()
152 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
153 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
155 local function tag_fields(e)
156 if e.option and node.fields[e.option] then
157 if node.fields[e.option].error then
158 node.fields[e.option].error[name] = e
160 node.fields[e.option].error = { [name] = e }
163 for _, c in ipairs(e.childs) do tag_fields(c) end
167 local function tag_section(e)
169 for _, c in ipairs(e.childs) do
170 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
171 table.insert( s, c.childs[1]:string() )
173 table.insert( s, c:string() )
180 node.error = { [name] = s }
185 local stat, err = node.map.validator:validate_section(node.config, name, co)
187 node.map.save = false
194 local function _uvl_strip_remote_dependencies(deps)
197 for k, v in pairs(deps) do
198 k = k:gsub("%$config%.%$section%.", "")
199 if k:match("^[%w_]+$") and type(v) == "string" then
208 -- Node pseudo abstract class
211 function Node.__init__(self, title, description)
213 self.title = title or ""
214 self.description = description or ""
215 self.template = "cbi/node"
219 function Node._i18n(self, config, section, option, title, description)
222 if type(luci.i18n) == "table" then
224 local key = config and config:gsub("[^%w]+", "") or ""
226 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
227 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
229 self.title = title or luci.i18n.translate( key, option or section or config )
230 self.description = description or luci.i18n.translate( key .. "_desc", "" )
235 function Node.prepare(self, ...)
236 for k, child in ipairs(self.children) do
241 -- Append child nodes
242 function Node.append(self, obj)
243 table.insert(self.children, obj)
246 -- Parse this node and its children
247 function Node.parse(self, ...)
248 for k, child in ipairs(self.children) do
254 function Node.render(self, scope)
258 luci.template.render(self.template, scope)
261 -- Render the children
262 function Node.render_children(self, ...)
263 for k, node in ipairs(self.children) do
270 A simple template element
272 Template = class(Node)
274 function Template.__init__(self, template)
276 self.template = template
279 function Template.render(self)
280 luci.template.render(self.template, {self=self})
285 Map - A map describing a configuration file
289 function Map.__init__(self, config, ...)
290 Node.__init__(self, ...)
291 Node._i18n(self, config, nil, nil, ...)
294 self.parsechain = {self.config}
295 self.template = "cbi/map"
296 self.apply_on_parse = nil
297 self.readinput = true
299 self.uci = uci.cursor()
304 if not self.uci:load(self.config) then
305 error("Unable to read UCI data: " .. self.config)
308 self.validator = luci.uvl.UVL()
309 self.scheme = self.validator:get_scheme(self.config)
313 function Map.formvalue(self, key)
314 return self.readinput and luci.http.formvalue(key)
317 function Map.formvaluetable(self, key)
318 return self.readinput and luci.http.formvaluetable(key)
321 function Map.get_scheme(self, sectiontype, option)
323 return self.scheme and self.scheme.sections[sectiontype]
325 return self.scheme and self.scheme.variables[sectiontype]
326 and self.scheme.variables[sectiontype][option]
330 function Map.submitstate(self)
331 return self:formvalue("cbi.submit")
334 -- Chain foreign config
335 function Map.chain(self, config)
336 table.insert(self.parsechain, config)
339 function Map.state_handler(self, state)
343 -- Use optimized UCI writing
344 function Map.parse(self, readinput, ...)
345 self.readinput = (readinput ~= false)
346 Node.parse(self, ...)
349 for i, config in ipairs(self.parsechain) do
350 self.uci:save(config)
352 if self:submitstate() and (self.autoapply or luci.http.formvalue("cbi.apply")) then
353 for i, config in ipairs(self.parsechain) do
354 self.uci:commit(config)
356 -- Refresh data because commit changes section names
357 self.uci:load(config)
359 if self.apply_on_parse then
360 self.uci:apply(self.parsechain)
362 self._apply = function()
363 local cmd = self.uci:apply(self.parsechain, true)
369 Node.parse(self, true)
372 for i, config in ipairs(self.parsechain) do
373 self.uci:unload(config)
375 if type(self.commit_handler) == "function" then
376 self:commit_handler(self:submitstate())
380 if self:submitstate() then
382 self.state = self.changed and FORM_CHANGED or FORM_VALID
384 self.state = FORM_INVALID
387 self.state = FORM_NODATA
390 return self:state_handler(self.state)
393 function Map.render(self, ...)
394 Node.render(self, ...)
396 local fp = self._apply()
402 -- Creates a child section
403 function Map.section(self, class, ...)
404 if instanceof(class, AbstractSection) then
405 local obj = class(self, ...)
409 error("class must be a descendent of AbstractSection")
414 function Map.add(self, sectiontype)
415 return self.uci:add(self.config, sectiontype)
419 function Map.set(self, section, option, value)
421 return self.uci:set(self.config, section, option, value)
423 return self.uci:set(self.config, section, value)
428 function Map.del(self, section, option)
430 return self.uci:delete(self.config, section, option)
432 return self.uci:delete(self.config, section)
437 function Map.get(self, section, option)
439 return self.uci:get_all(self.config)
441 return self.uci:get(self.config, section, option)
443 return self.uci:get_all(self.config, section)
450 Compound = class(Node)
452 function Compound.__init__(self, ...)
454 self.children = {...}
457 function Compound.parse(self, ...)
458 local cstate, state = 0, 0
460 for k, child in ipairs(self.children) do
461 cstate = child:parse(...)
462 state = (not state or cstate < state) and cstate or state
470 Delegator - Node controller
472 Delegator = class(Node)
473 function Delegator.__init__(self, ...)
474 Node.__init__(self, ...)
476 self.template = "cbi/delegator"
479 function Delegator.state(self, name, node, transitor)
480 transitor = transitor or self.transistor_linear
481 local state = {node=node, name=name, transitor=transitor}
483 assert(instanceof(node, Node), "Invalid node")
484 assert(not self.nodes[name], "Duplicate entry")
486 self.nodes[name] = state
492 function Delegator.get(self, name)
493 return self.nodes[name]
496 function Delegator.transistor_linear(self, state, cstate)
498 for i, child in ipairs(self.children) do
499 if state == child then
500 return self.children[i+1]
508 function Delegator.parse(self, ...)
509 local active = self:getactive()
510 assert(active, "Invalid state")
512 local cstate = active.node:parse()
513 self.active = active.transistor(self, active.node, cstate)
515 if not self.active then
518 self.active:parse(false)
523 function Delegator.render(self, ...)
524 self.active.node:render(...)
527 function Delegator.getactive(self)
528 return self:get(Map.formvalue(self, "cbi.delegated")
529 or (self.children[1] and self.children[1].name))
537 Page.__init__ = Node.__init__
538 Page.parse = function() end
542 SimpleForm - A Simple non-UCI form
544 SimpleForm = class(Node)
546 function SimpleForm.__init__(self, config, title, description, data)
547 Node.__init__(self, title, description)
549 self.data = data or {}
550 self.template = "cbi/simpleform"
552 self.pageaction = false
553 self.readinput = true
556 SimpleForm.formvalue = Map.formvalue
557 SimpleForm.formvaluetable = Map.formvaluetable
559 function SimpleForm.parse(self, readinput, ...)
560 self.readinput = (readinput ~= false)
561 if self:submitstate() then
562 Node.parse(self, 1, ...)
566 for k, j in ipairs(self.children) do
567 for i, v in ipairs(j.children) do
569 and (not v.tag_missing or not v.tag_missing[1])
570 and (not v.tag_invalid or not v.tag_invalid[1])
576 not self:submitstate() and FORM_NODATA
577 or valid and FORM_VALID
580 self.dorender = not self.handle or self:handle(state, self.data) ~= false
584 function SimpleForm.render(self, ...)
585 if self.dorender then
586 Node.render(self, ...)
590 function SimpleForm.submitstate(self)
591 return self:formvalue("cbi.submit")
594 function SimpleForm.section(self, class, ...)
595 if instanceof(class, AbstractSection) then
596 local obj = class(self, ...)
600 error("class must be a descendent of AbstractSection")
604 -- Creates a child field
605 function SimpleForm.field(self, class, ...)
607 for k, v in ipairs(self.children) do
608 if instanceof(v, SimpleSection) then
614 section = self:section(SimpleSection)
617 if instanceof(class, AbstractValue) then
618 local obj = class(self, section, ...)
619 obj.track_missing = true
623 error("class must be a descendent of AbstractValue")
627 function SimpleForm.set(self, section, option, value)
628 self.data[option] = value
632 function SimpleForm.del(self, section, option)
633 self.data[option] = nil
637 function SimpleForm.get(self, section, option)
638 return self.data[option]
642 function SimpleForm.get_scheme()
647 Form = class(SimpleForm)
649 function Form.__init__(self, ...)
650 SimpleForm.__init__(self, ...)
658 AbstractSection = class(Node)
660 function AbstractSection.__init__(self, map, sectiontype, ...)
661 Node.__init__(self, ...)
662 self.sectiontype = sectiontype
664 self.config = map.config
669 self.tag_invalid = {}
670 self.tag_deperror = {}
674 self.addremove = false
678 -- Appends a new option
679 function AbstractSection.option(self, class, option, ...)
680 -- Autodetect from UVL
681 if class == true and self.map:get_scheme(self.sectiontype, option) then
682 local vs = self.map:get_scheme(self.sectiontype, option)
683 if vs.type == "boolean" then
685 elseif vs.type == "list" then
687 elseif vs.type == "enum" or vs.type == "reference" then
694 if instanceof(class, AbstractValue) then
695 local obj = class(self.map, self, option, ...)
697 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
700 self.fields[option] = obj
702 elseif class == true then
703 error("No valid class was given and autodetection failed.")
705 error("class must be a descendant of AbstractValue")
709 -- Parse optional options
710 function AbstractSection.parse_optionals(self, section)
711 if not self.optional then
715 self.optionals[section] = {}
717 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
718 for k,v in ipairs(self.children) do
719 if v.optional and not v:cfgvalue(section) then
720 if field == v.option then
723 table.insert(self.optionals[section], v)
728 if field and #field > 0 and self.dynamic then
729 self:add_dynamic(field)
733 -- Add a dynamic option
734 function AbstractSection.add_dynamic(self, field, optional)
735 local o = self:option(Value, field, field)
736 o.optional = optional
739 -- Parse all dynamic options
740 function AbstractSection.parse_dynamic(self, section)
741 if not self.dynamic then
745 local arr = luci.util.clone(self:cfgvalue(section))
746 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
747 for k, v in pairs(form) do
751 for key,val in pairs(arr) do
754 for i,c in ipairs(self.children) do
755 if c.option == key then
760 if create and key:sub(1, 1) ~= "." then
761 self:add_dynamic(key, true)
766 -- Returns the section's UCI table
767 function AbstractSection.cfgvalue(self, section)
768 return self.map:get(section)
772 function AbstractSection.push_events(self)
773 --luci.util.append(self.map.events, self.events)
774 self.map.changed = true
777 -- Removes the section
778 function AbstractSection.remove(self, section)
779 self.map.autoapply = false
780 return self.map:del(section)
783 -- Creates the section
784 function AbstractSection.create(self, section)
788 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
790 section = self.map:add(self.sectiontype)
795 for k,v in pairs(self.children) do
797 self.map:set(section, v.option, v.default)
801 for k,v in pairs(self.defaults) do
802 self.map:set(section, k, v)
806 self.map.autoapply = false
812 SimpleSection = class(AbstractSection)
814 function SimpleSection.__init__(self, form, ...)
815 AbstractSection.__init__(self, form, nil, ...)
816 self.template = "cbi/nullsection"
820 Table = class(AbstractSection)
822 function Table.__init__(self, form, data, ...)
823 local datasource = {}
825 datasource.config = "table"
826 self.data = data or {}
828 datasource.formvalue = Map.formvalue
829 datasource.formvaluetable = Map.formvaluetable
830 datasource.readinput = true
832 function datasource.get(self, section, option)
833 return tself.data[section] and tself.data[section][option]
836 function datasource.submitstate(self)
837 return Map.formvalue(self, "cbi.submit")
840 function datasource.del(...)
844 function datasource.get_scheme()
848 AbstractSection.__init__(self, datasource, "table", ...)
849 self.template = "cbi/tblsection"
850 self.rowcolors = true
851 self.anonymous = true
854 function Table.parse(self, readinput)
855 self.map.readinput = (readinput ~= false)
856 for i, k in ipairs(self:cfgsections()) do
857 if self.map:submitstate() then
863 function Table.cfgsections(self)
866 for i, v in luci.util.kspairs(self.data) do
867 table.insert(sections, i)
873 function Table.update(self, data)
880 NamedSection - A fixed configuration section defined by its name
882 NamedSection = class(AbstractSection)
884 function NamedSection.__init__(self, map, section, stype, ...)
885 AbstractSection.__init__(self, map, stype, ...)
886 Node._i18n(self, map.config, section, nil, ...)
889 self.addremove = false
891 -- Use defaults from UVL
892 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
893 local vs = self.map:get_scheme(self.sectiontype)
894 self.addremove = not vs.unique and not vs.required
895 self.dynamic = vs.dynamic
896 self.title = self.title or vs.title
897 self.description = self.description or vs.descr
900 self.template = "cbi/nsection"
901 self.section = section
904 function NamedSection.parse(self, novld)
905 local s = self.section
906 local active = self:cfgvalue(s)
908 if self.addremove then
909 local path = self.config.."."..s
910 if active then -- Remove the section
911 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
915 else -- Create and apply default values
916 if self.map:formvalue("cbi.cns."..path) then
924 AbstractSection.parse_dynamic(self, s)
925 if self.map:submitstate() then
928 if not novld and not self.override_scheme and self.map.scheme then
929 _uvl_validate_section(self, s)
932 AbstractSection.parse_optionals(self, s)
942 TypedSection - A (set of) configuration section(s) defined by the type
943 addremove: Defines whether the user can add/remove sections of this type
944 anonymous: Allow creating anonymous sections
945 validate: a validation function returning nil if the section is invalid
947 TypedSection = class(AbstractSection)
949 function TypedSection.__init__(self, map, type, ...)
950 AbstractSection.__init__(self, map, type, ...)
951 Node._i18n(self, map.config, type, nil, ...)
953 self.template = "cbi/tsection"
955 self.anonymous = false
957 -- Use defaults from UVL
958 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
959 local vs = self.map:get_scheme(self.sectiontype)
960 self.addremove = not vs.unique and not vs.required
961 self.dynamic = vs.dynamic
962 self.anonymous = not vs.named
963 self.title = self.title or vs.title
964 self.description = self.description or vs.descr
968 -- Return all matching UCI sections for this TypedSection
969 function TypedSection.cfgsections(self)
971 self.map.uci:foreach(self.map.config, self.sectiontype,
973 if self:checkscope(section[".name"]) then
974 table.insert(sections, section[".name"])
981 -- Limits scope to sections that have certain option => value pairs
982 function TypedSection.depends(self, option, value)
983 table.insert(self.deps, {option=option, value=value})
986 function TypedSection.parse(self, novld)
987 if self.addremove then
989 local crval = REMOVE_PREFIX .. self.config
990 local name = self.map:formvaluetable(crval)
991 for k,v in pairs(name) do
992 if k:sub(-2) == ".x" then
995 if self:cfgvalue(k) and self:checkscope(k) then
1002 for i, k in ipairs(self:cfgsections()) do
1003 AbstractSection.parse_dynamic(self, k)
1004 if self.map:submitstate() then
1005 Node.parse(self, k, novld)
1007 if not novld and not self.override_scheme and self.map.scheme then
1008 _uvl_validate_section(self, k)
1011 AbstractSection.parse_optionals(self, k)
1014 if self.addremove then
1017 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1018 local name = self.map:formvalue(crval)
1019 if self.anonymous then
1021 created = self:create()
1025 -- Ignore if it already exists
1026 if self:cfgvalue(name) then
1030 name = self:checkscope(name)
1033 self.err_invalid = true
1036 if name and #name > 0 then
1037 created = self:create(name) and name
1039 self.invalid_cts = true
1046 AbstractSection.parse_optionals(self, created)
1050 if created or self.changed then
1055 -- Verifies scope of sections
1056 function TypedSection.checkscope(self, section)
1057 -- Check if we are not excluded
1058 if self.filter and not self:filter(section) then
1062 -- Check if at least one dependency is met
1063 if #self.deps > 0 and self:cfgvalue(section) then
1066 for k, v in ipairs(self.deps) do
1067 if self:cfgvalue(section)[v.option] == v.value then
1077 return self:validate(section)
1081 -- Dummy validate function
1082 function TypedSection.validate(self, section)
1088 AbstractValue - An abstract Value Type
1089 null: Value can be empty
1090 valid: A function returning the value if it is valid otherwise nil
1091 depends: A table of option => value pairs of which one must be true
1092 default: The default value
1093 size: The size of the input fields
1094 rmempty: Unset value if empty
1095 optional: This value is optional (see AbstractSection.optionals)
1097 AbstractValue = class(Node)
1099 function AbstractValue.__init__(self, map, section, option, ...)
1100 Node.__init__(self, ...)
1101 self.section = section
1102 self.option = option
1104 self.config = map.config
1105 self.tag_invalid = {}
1106 self.tag_missing = {}
1107 self.tag_reqerror = {}
1110 --self.cast = "string"
1112 self.track_missing = false
1116 self.optional = false
1119 function AbstractValue.prepare(self)
1120 -- Use defaults from UVL
1121 if not self.override_scheme
1122 and self.map:get_scheme(self.section.sectiontype, self.option) then
1123 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1124 if self.cast == nil then
1125 self.cast = (vs.type == "list") and "list" or "string"
1127 self.title = self.title or vs.title
1128 self.description = self.description or vs.descr
1129 if self.default == nil then
1130 self.default = vs.default
1133 if vs.depends and not self.override_dependencies then
1134 for i, deps in ipairs(vs.depends) do
1135 deps = _uvl_strip_remote_dependencies(deps)
1143 self.cast = self.cast or "string"
1146 -- Add a dependencie to another section field
1147 function AbstractValue.depends(self, field, value)
1149 if type(field) == "string" then
1156 table.insert(self.deps, {deps=deps, add=""})
1159 -- Generates the unique CBID
1160 function AbstractValue.cbid(self, section)
1161 return "cbid."..self.map.config.."."..section.."."..self.option
1164 -- Return whether this object should be created
1165 function AbstractValue.formcreated(self, section)
1166 local key = "cbi.opt."..self.config.."."..section
1167 return (self.map:formvalue(key) == self.option)
1170 -- Returns the formvalue for this object
1171 function AbstractValue.formvalue(self, section)
1172 return self.map:formvalue(self:cbid(section))
1175 function AbstractValue.additional(self, value)
1176 self.optional = value
1179 function AbstractValue.mandatory(self, value)
1180 self.rmempty = not value
1183 function AbstractValue.parse(self, section, novld)
1184 local fvalue = self:formvalue(section)
1185 local cvalue = self:cfgvalue(section)
1187 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1188 fvalue = self:transform(self:validate(fvalue, section))
1189 if not fvalue and not novld then
1191 self.error[section] = "invalid"
1193 self.error = { [section] = "invalid" }
1195 self.map.save = false
1197 if fvalue and not (fvalue == cvalue) then
1198 if self:write(section, fvalue) then
1200 self.section.changed = true
1201 --luci.util.append(self.map.events, self.events)
1204 else -- Unset the UCI or error
1205 if self.rmempty or self.optional then
1206 if self:remove(section) then
1208 self.section.changed = true
1209 --luci.util.append(self.map.events, self.events)
1211 elseif cvalue ~= fvalue and not novld then
1212 self:write(section, fvalue or "")
1214 self.error[section] = "missing"
1216 self.error = { [section] = "missing" }
1218 self.map.save = false
1223 -- Render if this value exists or if it is mandatory
1224 function AbstractValue.render(self, s, scope)
1225 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1228 scope.cbid = self:cbid(s)
1229 scope.striptags = luci.util.striptags
1231 scope.ifattr = function(cond,key,val)
1233 return string.format(
1234 ' %s="%s"', tostring(key),
1235 luci.util.pcdata(tostring( val
1237 or (type(self[key]) ~= "function" and self[key])
1245 scope.attr = function(...)
1246 return scope.ifattr( true, ... )
1249 Node.render(self, scope)
1253 -- Return the UCI value of this object
1254 function AbstractValue.cfgvalue(self, section)
1255 local value = self.map:get(section, self.option)
1258 elseif not self.cast or self.cast == type(value) then
1260 elseif self.cast == "string" then
1261 if type(value) == "table" then
1264 elseif self.cast == "table" then
1265 return luci.util.split(value, "%s+", nil, true)
1269 -- Validate the form value
1270 function AbstractValue.validate(self, value)
1274 AbstractValue.transform = AbstractValue.validate
1278 function AbstractValue.write(self, section, value)
1279 return self.map:set(section, self.option, value)
1283 function AbstractValue.remove(self, section)
1284 return self.map:del(section, self.option)
1291 Value - A one-line value
1292 maxlength: The maximum length
1294 Value = class(AbstractValue)
1296 function Value.__init__(self, ...)
1297 AbstractValue.__init__(self, ...)
1298 self.template = "cbi/value"
1303 function Value.value(self, key, val)
1305 table.insert(self.keylist, tostring(key))
1306 table.insert(self.vallist, tostring(val))
1310 -- DummyValue - This does nothing except being there
1311 DummyValue = class(AbstractValue)
1313 function DummyValue.__init__(self, ...)
1314 AbstractValue.__init__(self, ...)
1315 self.template = "cbi/dvalue"
1319 function DummyValue.cfgvalue(self, section)
1322 if type(self.value) == "function" then
1323 value = self:value(section)
1328 value = AbstractValue.cfgvalue(self, section)
1333 function DummyValue.parse(self)
1339 Flag - A flag being enabled or disabled
1341 Flag = class(AbstractValue)
1343 function Flag.__init__(self, ...)
1344 AbstractValue.__init__(self, ...)
1345 self.template = "cbi/fvalue"
1351 -- A flag can only have two states: set or unset
1352 function Flag.parse(self, section)
1353 local fvalue = self:formvalue(section)
1356 fvalue = self.enabled
1358 fvalue = self.disabled
1361 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1362 if not(fvalue == self:cfgvalue(section)) then
1363 self:write(section, fvalue)
1366 self:remove(section)
1373 ListValue - A one-line value predefined in a list
1374 widget: The widget that will be used (select, radio)
1376 ListValue = class(AbstractValue)
1378 function ListValue.__init__(self, ...)
1379 AbstractValue.__init__(self, ...)
1380 self.template = "cbi/lvalue"
1385 self.widget = "select"
1388 function ListValue.prepare(self, ...)
1389 AbstractValue.prepare(self, ...)
1390 if not self.override_scheme
1391 and self.map:get_scheme(self.section.sectiontype, self.option) then
1392 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1393 if self.value and vs.valuelist and not self.override_values then
1394 for k, v in ipairs(vs.valuelist) do
1396 if not self.override_dependencies
1397 and vs.enum_depends and vs.enum_depends[v.value] then
1398 for i, dep in ipairs(vs.enum_depends[v.value]) do
1399 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1402 self:value(v.value, v.title or v.value, unpack(deps))
1408 function ListValue.value(self, key, val, ...)
1409 if luci.util.contains(self.keylist, key) then
1414 table.insert(self.keylist, tostring(key))
1415 table.insert(self.vallist, tostring(val))
1417 for i, deps in ipairs({...}) do
1418 table.insert(self.deps, {add = "-"..key, deps=deps})
1422 function ListValue.validate(self, val)
1423 if luci.util.contains(self.keylist, val) then
1433 MultiValue - Multiple delimited values
1434 widget: The widget that will be used (select, checkbox)
1435 delimiter: The delimiter that will separate the values (default: " ")
1437 MultiValue = class(AbstractValue)
1439 function MultiValue.__init__(self, ...)
1440 AbstractValue.__init__(self, ...)
1441 self.template = "cbi/mvalue"
1446 self.widget = "checkbox"
1447 self.delimiter = " "
1450 function MultiValue.render(self, ...)
1451 if self.widget == "select" and not self.size then
1452 self.size = #self.vallist
1455 AbstractValue.render(self, ...)
1458 function MultiValue.value(self, key, val)
1459 if luci.util.contains(self.keylist, key) then
1464 table.insert(self.keylist, tostring(key))
1465 table.insert(self.vallist, tostring(val))
1468 function MultiValue.valuelist(self, section)
1469 local val = self:cfgvalue(section)
1471 if not(type(val) == "string") then
1475 return luci.util.split(val, self.delimiter)
1478 function MultiValue.validate(self, val)
1479 val = (type(val) == "table") and val or {val}
1483 for i, value in ipairs(val) do
1484 if luci.util.contains(self.keylist, value) then
1485 result = result and (result .. self.delimiter .. value) or value
1493 StaticList = class(MultiValue)
1495 function StaticList.__init__(self, ...)
1496 MultiValue.__init__(self, ...)
1498 self.valuelist = self.cfgvalue
1500 if not self.override_scheme
1501 and self.map:get_scheme(self.section.sectiontype, self.option) then
1502 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1503 if self.value and vs.values and not self.override_values then
1504 for k, v in pairs(vs.values) do
1511 function StaticList.validate(self, value)
1512 value = (type(value) == "table") and value or {value}
1515 for i, v in ipairs(value) do
1516 if luci.util.contains(self.keylist, v) then
1517 table.insert(valid, v)
1524 DynamicList = class(AbstractValue)
1526 function DynamicList.__init__(self, ...)
1527 AbstractValue.__init__(self, ...)
1528 self.template = "cbi/dynlist"
1534 function DynamicList.value(self, key, val)
1536 table.insert(self.keylist, tostring(key))
1537 table.insert(self.vallist, tostring(val))
1540 function DynamicList.formvalue(self, section)
1541 local value = AbstractValue.formvalue(self, section)
1542 value = (type(value) == "table") and value or {value}
1545 for i, v in ipairs(value) do
1547 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1548 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1549 table.insert(valid, v)
1558 TextValue - A multi-line value
1561 TextValue = class(AbstractValue)
1563 function TextValue.__init__(self, ...)
1564 AbstractValue.__init__(self, ...)
1565 self.template = "cbi/tvalue"
1571 Button = class(AbstractValue)
1573 function Button.__init__(self, ...)
1574 AbstractValue.__init__(self, ...)
1575 self.template = "cbi/button"
1576 self.inputstyle = nil
1581 FileUpload = class(AbstractValue)
1583 function FileUpload.__init__(self, ...)
1584 AbstractValue.__init__(self, ...)
1585 self.template = "cbi/upload"
1586 if not self.map.upload_fields then
1587 self.map.upload_fields = { self }
1589 self.map.upload_fields[#self.map.upload_fields+1] = self
1593 function FileUpload.formcreated(self, section)
1594 return AbstractValue.formcreated(self, section) or
1595 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1596 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1599 function FileUpload.cfgvalue(self, section)
1600 local val = AbstractValue.cfgvalue(self, section)
1601 if val and luci.fs.access(val) then
1607 function FileUpload.formvalue(self, section)
1608 local val = AbstractValue.formvalue(self, section)
1610 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1611 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1621 function FileUpload.remove(self, section)
1622 local val = AbstractValue.formvalue(self, section)
1623 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1624 return AbstractValue.remove(self, section)
1628 FileBrowser = class(AbstractValue)
1630 function FileBrowser.__init__(self, ...)
1631 AbstractValue.__init__(self, ...)
1632 self.template = "cbi/browser"