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 fs = require("nixio.fs")
37 local uci = require("luci.model.uci")
38 local class = util.class
39 local instanceof = util.instanceof
51 CREATE_PREFIX = "cbi.cts."
52 REMOVE_PREFIX = "cbi.rts."
54 -- Loads a CBI map from given file, creating an environment and returns it
55 function load(cbimap, ...)
56 local fs = require "nixio.fs"
57 local i18n = require "luci.i18n"
58 require("luci.config")
61 local upldir = "/lib/uci/upload/"
62 local cbidir = luci.util.libpath() .. "/model/cbi/"
65 if fs.access(cbimap) then
66 func, err = loadfile(cbimap)
67 elseif fs.access(cbidir..cbimap..".lua") then
68 func, err = loadfile(cbidir..cbimap..".lua")
69 elseif fs.access(cbidir..cbimap..".lua.gz") then
70 func, err = loadfile(cbidir..cbimap..".lua.gz")
72 func, err = nil, "Model '" .. cbimap .. "' not found!"
77 luci.i18n.loadc("cbi")
78 luci.i18n.loadc("uvl")
81 translate=i18n.translate,
82 translatef=i18n.translatef,
86 setfenv(func, setmetatable(env, {__index =
88 return rawget(tbl, key) or _M[key] or _G[key]
91 local maps = { func() }
93 local has_upload = false
95 for i, map in ipairs(maps) do
96 if not instanceof(map, Node) then
97 error("CBI map returns no valid map object!")
101 if map.upload_fields then
103 for _, field in ipairs(map.upload_fields) do
105 field.config .. '.' ..
106 field.section.sectiontype .. '.' ..
115 local uci = luci.model.uci.cursor()
116 local prm = luci.http.context.request.message.params
119 luci.http.setfilehandler(
120 function( field, chunk, eof )
121 if not field then return end
122 if field.name and not cbid then
123 local c, s, o = field.name:gmatch(
124 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
127 if c and s and o then
128 local t = uci:get( c, s )
129 if t and uploads[c.."."..t.."."..o] then
130 local path = upldir .. field.name
131 fd = io.open(path, "w")
140 if field.name == cbid and fd then
156 local function _uvl_validate_section(node, name)
157 local co = node.map:get()
159 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
160 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
162 local function tag_fields(e)
163 if e.option and node.fields[e.option] then
164 if node.fields[e.option].error then
165 node.fields[e.option].error[name] = e
167 node.fields[e.option].error = { [name] = e }
170 for _, c in ipairs(e.childs) do tag_fields(c) end
174 local function tag_section(e)
176 for _, c in ipairs(e.childs or { e }) do
177 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
178 table.insert( s, c.childs[1]:string() )
180 table.insert( s, c:string() )
187 node.error = { [name] = s }
192 local stat, err = node.map.validator:validate_section(node.config, name, co)
194 node.map.save = false
201 local function _uvl_strip_remote_dependencies(deps)
204 for k, v in pairs(deps) do
205 k = k:gsub("%$config%.%$section%.", "")
206 if k:match("^[%w_]+$") and type(v) == "string" then
215 -- Node pseudo abstract class
218 function Node.__init__(self, title, description)
220 self.title = title or ""
221 self.description = description or ""
222 self.template = "cbi/node"
226 function Node._i18n(self, config, section, option, title, description)
229 if type(luci.i18n) == "table" then
231 local key = config and config:gsub("[^%w]+", "") or ""
233 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
234 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
236 self.title = title or luci.i18n.translate( key, option or section or config )
237 self.description = description or luci.i18n.translate( key .. "_desc", "" )
242 function Node.prepare(self, ...)
243 for k, child in ipairs(self.children) do
248 -- Append child nodes
249 function Node.append(self, obj)
250 table.insert(self.children, obj)
253 -- Parse this node and its children
254 function Node.parse(self, ...)
255 for k, child in ipairs(self.children) do
261 function Node.render(self, scope)
265 luci.template.render(self.template, scope)
268 -- Render the children
269 function Node.render_children(self, ...)
270 for k, node in ipairs(self.children) do
277 A simple template element
279 Template = class(Node)
281 function Template.__init__(self, template)
283 self.template = template
286 function Template.render(self)
287 luci.template.render(self.template, {self=self})
290 function Template.parse(self, readinput)
291 self.readinput = (readinput ~= false)
292 return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
297 Map - A map describing a configuration file
301 function Map.__init__(self, config, ...)
302 Node.__init__(self, ...)
303 Node._i18n(self, config, nil, nil, ...)
306 self.parsechain = {self.config}
307 self.template = "cbi/map"
308 self.apply_on_parse = nil
309 self.readinput = true
313 self.uci = uci.cursor()
318 if not self.uci:load(self.config) then
319 error("Unable to read UCI data: " .. self.config)
322 self.validator = luci.uvl.UVL()
323 self.scheme = self.validator:get_scheme(self.config)
327 function Map.formvalue(self, key)
328 return self.readinput and luci.http.formvalue(key)
331 function Map.formvaluetable(self, key)
332 return self.readinput and luci.http.formvaluetable(key) or {}
335 function Map.get_scheme(self, sectiontype, option)
337 return self.scheme and self.scheme.sections[sectiontype]
339 return self.scheme and self.scheme.variables[sectiontype]
340 and self.scheme.variables[sectiontype][option]
344 function Map.submitstate(self)
345 return self:formvalue("cbi.submit")
348 -- Chain foreign config
349 function Map.chain(self, config)
350 table.insert(self.parsechain, config)
353 function Map.state_handler(self, state)
357 -- Use optimized UCI writing
358 function Map.parse(self, readinput, ...)
359 self.readinput = (readinput ~= false)
361 if self:formvalue("cbi.skip") then
362 self.state = FORM_SKIP
363 return self:state_handler(self.state)
366 Node.parse(self, ...)
369 for i, config in ipairs(self.parsechain) do
370 self.uci:save(config)
372 if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
373 for i, config in ipairs(self.parsechain) do
374 self.uci:commit(config)
376 -- Refresh data because commit changes section names
377 self.uci:load(config)
379 if self.apply_on_parse then
380 self.uci:apply(self.parsechain)
382 self._apply = function()
383 local cmd = self.uci:apply(self.parsechain, true)
389 Node.parse(self, true)
392 for i, config in ipairs(self.parsechain) do
393 self.uci:unload(config)
395 if type(self.commit_handler) == "function" then
396 self:commit_handler(self:submitstate())
400 if self:submitstate() then
401 if not self.save then
402 self.state = FORM_INVALID
403 elseif self.proceed then
404 self.state = FORM_PROCEED
406 self.state = self.changed and FORM_CHANGED or FORM_VALID
409 self.state = FORM_NODATA
412 return self:state_handler(self.state)
415 function Map.render(self, ...)
416 Node.render(self, ...)
418 local fp = self._apply()
424 -- Creates a child section
425 function Map.section(self, class, ...)
426 if instanceof(class, AbstractSection) then
427 local obj = class(self, ...)
431 error("class must be a descendent of AbstractSection")
436 function Map.add(self, sectiontype)
437 return self.uci:add(self.config, sectiontype)
441 function Map.set(self, section, option, value)
443 return self.uci:set(self.config, section, option, value)
445 return self.uci:set(self.config, section, value)
450 function Map.del(self, section, option)
452 return self.uci:delete(self.config, section, option)
454 return self.uci:delete(self.config, section)
459 function Map.get(self, section, option)
461 return self.uci:get_all(self.config)
463 return self.uci:get(self.config, section, option)
465 return self.uci:get_all(self.config, section)
472 Compound = class(Node)
474 function Compound.__init__(self, ...)
476 self.template = "cbi/compound"
477 self.children = {...}
480 function Compound.populate_delegator(self, delegator)
481 for _, v in ipairs(self.children) do
482 v.delegator = delegator
486 function Compound.parse(self, ...)
487 local cstate, state = 0
489 for k, child in ipairs(self.children) do
490 cstate = child:parse(...)
491 state = (not state or cstate < state) and cstate or state
499 Delegator - Node controller
501 Delegator = class(Node)
502 function Delegator.__init__(self, ...)
503 Node.__init__(self, ...)
505 self.defaultpath = {}
506 self.pageaction = false
507 self.readinput = true
508 self.allow_reset = false
509 self.allow_back = false
510 self.allow_finish = false
511 self.template = "cbi/delegator"
514 function Delegator.set(self, name, node)
515 if type(node) == "table" and getmetatable(node) == nil then
516 node = Compound(unpack(node))
518 assert(type(node) == "function" or instanceof(node, Compound), "Invalid")
519 assert(not self.nodes[name], "Duplicate entry")
521 self.nodes[name] = node
524 function Delegator.add(self, name, node)
525 node = self:set(name, node)
526 self.defaultpath[#self.defaultpath+1] = name
529 function Delegator.insert_after(self, name, after)
530 local n = #self.chain
531 for k, v in ipairs(self.chain) do
537 table.insert(self.chain, n, name)
540 function Delegator.set_route(self, ...)
541 local n, chain, route = 0, self.chain, {...}
543 if chain[i] == self.current then
552 for i = n + 1, #chain do
557 function Delegator.get(self, name)
558 return self.nodes[name]
561 function Delegator.parse(self, ...)
563 self.chain = self.chain or self:get_chain()
564 self.current = self.current or self:get_active()
565 self.active = self.active or self:get(self.current)
566 assert(self.active, "Invalid state")
568 local stat = FORM_DONE
569 if type(self.active) ~= "function" then
570 self.active:populate_delegator(self)
571 stat = self.active:parse()
576 if stat > FORM_PROCEED then
577 if Map.formvalue(self, "cbi.delg.back") then
578 newcurrent = self:get_prev(self.current)
580 newcurrent = self:get_next(self.current)
582 elseif stat < FORM_PROCEED then
587 if not Map.formvalue(self, "cbi.submit") then
589 elseif not newcurrent or not self:get(newcurrent) then
592 self.current = newcurrent
593 self.active = self:get(self.current)
594 if type(self.active) ~= "function" then
595 self.active:parse(false)
598 return self:parse(...)
603 function Delegator.get_next(self, state)
604 for k, v in ipairs(self.chain) do
606 return self.chain[k+1]
611 function Delegator.get_prev(self, state)
612 for k, v in ipairs(self.chain) do
614 return self.chain[k-1]
619 function Delegator.get_chain(self)
620 local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
621 return type(x) == "table" and x or {x}
624 function Delegator.get_active(self)
625 return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
633 Page.__init__ = Node.__init__
634 Page.parse = function() end
638 SimpleForm - A Simple non-UCI form
640 SimpleForm = class(Node)
642 function SimpleForm.__init__(self, config, title, description, data)
643 Node.__init__(self, title, description)
645 self.data = data or {}
646 self.template = "cbi/simpleform"
648 self.pageaction = false
649 self.readinput = true
652 SimpleForm.formvalue = Map.formvalue
653 SimpleForm.formvaluetable = Map.formvaluetable
655 function SimpleForm.parse(self, readinput, ...)
656 self.readinput = (readinput ~= false)
658 if self:formvalue("cbi.skip") then
662 if self:submitstate() then
663 Node.parse(self, 1, ...)
667 for k, j in ipairs(self.children) do
668 for i, v in ipairs(j.children) do
670 and (not v.tag_missing or not v.tag_missing[1])
671 and (not v.tag_invalid or not v.tag_invalid[1])
677 not self:submitstate() and FORM_NODATA
678 or valid and FORM_VALID
681 self.dorender = not self.handle
683 local nrender, nstate = self:handle(state, self.data)
684 self.dorender = self.dorender or (nrender ~= false)
685 state = nstate or state
690 function SimpleForm.render(self, ...)
691 if self.dorender then
692 Node.render(self, ...)
696 function SimpleForm.submitstate(self)
697 return self:formvalue("cbi.submit")
700 function SimpleForm.section(self, class, ...)
701 if instanceof(class, AbstractSection) then
702 local obj = class(self, ...)
706 error("class must be a descendent of AbstractSection")
710 -- Creates a child field
711 function SimpleForm.field(self, class, ...)
713 for k, v in ipairs(self.children) do
714 if instanceof(v, SimpleSection) then
720 section = self:section(SimpleSection)
723 if instanceof(class, AbstractValue) then
724 local obj = class(self, section, ...)
725 obj.track_missing = true
729 error("class must be a descendent of AbstractValue")
733 function SimpleForm.set(self, section, option, value)
734 self.data[option] = value
738 function SimpleForm.del(self, section, option)
739 self.data[option] = nil
743 function SimpleForm.get(self, section, option)
744 return self.data[option]
748 function SimpleForm.get_scheme()
753 Form = class(SimpleForm)
755 function Form.__init__(self, ...)
756 SimpleForm.__init__(self, ...)
764 AbstractSection = class(Node)
766 function AbstractSection.__init__(self, map, sectiontype, ...)
767 Node.__init__(self, ...)
768 self.sectiontype = sectiontype
770 self.config = map.config
775 self.tag_invalid = {}
776 self.tag_deperror = {}
780 self.addremove = false
784 -- Appends a new option
785 function AbstractSection.option(self, class, option, ...)
786 -- Autodetect from UVL
787 if class == true and self.map:get_scheme(self.sectiontype, option) then
788 local vs = self.map:get_scheme(self.sectiontype, option)
789 if vs.type == "boolean" then
791 elseif vs.type == "list" then
793 elseif vs.type == "enum" or vs.type == "reference" then
800 if instanceof(class, AbstractValue) then
801 local obj = class(self.map, self, option, ...)
803 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
806 self.fields[option] = obj
808 elseif class == true then
809 error("No valid class was given and autodetection failed.")
811 error("class must be a descendant of AbstractValue")
815 -- Parse optional options
816 function AbstractSection.parse_optionals(self, section)
817 if not self.optional then
821 self.optionals[section] = {}
823 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
824 for k,v in ipairs(self.children) do
825 if v.optional and not v:cfgvalue(section) then
826 if field == v.option then
828 self.map.proceed = true
830 table.insert(self.optionals[section], v)
835 if field and #field > 0 and self.dynamic then
836 self:add_dynamic(field)
840 -- Add a dynamic option
841 function AbstractSection.add_dynamic(self, field, optional)
842 local o = self:option(Value, field, field)
843 o.optional = optional
846 -- Parse all dynamic options
847 function AbstractSection.parse_dynamic(self, section)
848 if not self.dynamic then
852 local arr = luci.util.clone(self:cfgvalue(section))
853 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
854 for k, v in pairs(form) do
858 for key,val in pairs(arr) do
861 for i,c in ipairs(self.children) do
862 if c.option == key then
867 if create and key:sub(1, 1) ~= "." then
868 self.map.proceed = true
869 self:add_dynamic(key, true)
874 -- Returns the section's UCI table
875 function AbstractSection.cfgvalue(self, section)
876 return self.map:get(section)
880 function AbstractSection.push_events(self)
881 --luci.util.append(self.map.events, self.events)
882 self.map.changed = true
885 -- Removes the section
886 function AbstractSection.remove(self, section)
887 self.map.proceed = true
888 return self.map:del(section)
891 -- Creates the section
892 function AbstractSection.create(self, section)
896 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
898 section = self.map:add(self.sectiontype)
903 for k,v in pairs(self.children) do
905 self.map:set(section, v.option, v.default)
909 for k,v in pairs(self.defaults) do
910 self.map:set(section, k, v)
914 self.map.proceed = true
920 SimpleSection = class(AbstractSection)
922 function SimpleSection.__init__(self, form, ...)
923 AbstractSection.__init__(self, form, nil, ...)
924 self.template = "cbi/nullsection"
928 Table = class(AbstractSection)
930 function Table.__init__(self, form, data, ...)
931 local datasource = {}
933 datasource.config = "table"
934 self.data = data or {}
936 datasource.formvalue = Map.formvalue
937 datasource.formvaluetable = Map.formvaluetable
938 datasource.readinput = true
940 function datasource.get(self, section, option)
941 return tself.data[section] and tself.data[section][option]
944 function datasource.submitstate(self)
945 return Map.formvalue(self, "cbi.submit")
948 function datasource.del(...)
952 function datasource.get_scheme()
956 AbstractSection.__init__(self, datasource, "table", ...)
957 self.template = "cbi/tblsection"
958 self.rowcolors = true
959 self.anonymous = true
962 function Table.parse(self, readinput)
963 self.map.readinput = (readinput ~= false)
964 for i, k in ipairs(self:cfgsections()) do
965 if self.map:submitstate() then
971 function Table.cfgsections(self)
974 for i, v in luci.util.kspairs(self.data) do
975 table.insert(sections, i)
981 function Table.update(self, data)
988 NamedSection - A fixed configuration section defined by its name
990 NamedSection = class(AbstractSection)
992 function NamedSection.__init__(self, map, section, stype, ...)
993 AbstractSection.__init__(self, map, stype, ...)
994 Node._i18n(self, map.config, section, nil, ...)
997 self.addremove = false
999 -- Use defaults from UVL
1000 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1001 local vs = self.map:get_scheme(self.sectiontype)
1002 self.addremove = not vs.unique and not vs.required
1003 self.dynamic = vs.dynamic
1004 self.title = self.title or vs.title
1005 self.description = self.description or vs.descr
1008 self.template = "cbi/nsection"
1009 self.section = section
1012 function NamedSection.parse(self, novld)
1013 local s = self.section
1014 local active = self:cfgvalue(s)
1016 if self.addremove then
1017 local path = self.config.."."..s
1018 if active then -- Remove the section
1019 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1023 else -- Create and apply default values
1024 if self.map:formvalue("cbi.cns."..path) then
1032 AbstractSection.parse_dynamic(self, s)
1033 if self.map:submitstate() then
1036 if not novld and not self.override_scheme and self.map.scheme then
1037 _uvl_validate_section(self, s)
1040 AbstractSection.parse_optionals(self, s)
1042 if self.changed then
1050 TypedSection - A (set of) configuration section(s) defined by the type
1051 addremove: Defines whether the user can add/remove sections of this type
1052 anonymous: Allow creating anonymous sections
1053 validate: a validation function returning nil if the section is invalid
1055 TypedSection = class(AbstractSection)
1057 function TypedSection.__init__(self, map, type, ...)
1058 AbstractSection.__init__(self, map, type, ...)
1059 Node._i18n(self, map.config, type, nil, ...)
1061 self.template = "cbi/tsection"
1063 self.anonymous = false
1065 -- Use defaults from UVL
1066 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1067 local vs = self.map:get_scheme(self.sectiontype)
1068 self.addremove = not vs.unique and not vs.required
1069 self.dynamic = vs.dynamic
1070 self.anonymous = not vs.named
1071 self.title = self.title or vs.title
1072 self.description = self.description or vs.descr
1076 -- Return all matching UCI sections for this TypedSection
1077 function TypedSection.cfgsections(self)
1079 self.map.uci:foreach(self.map.config, self.sectiontype,
1081 if self:checkscope(section[".name"]) then
1082 table.insert(sections, section[".name"])
1089 -- Limits scope to sections that have certain option => value pairs
1090 function TypedSection.depends(self, option, value)
1091 table.insert(self.deps, {option=option, value=value})
1094 function TypedSection.parse(self, novld)
1095 if self.addremove then
1097 local crval = REMOVE_PREFIX .. self.config
1098 local name = self.map:formvaluetable(crval)
1099 for k,v in pairs(name) do
1100 if k:sub(-2) == ".x" then
1101 k = k:sub(1, #k - 2)
1103 if self:cfgvalue(k) and self:checkscope(k) then
1110 for i, k in ipairs(self:cfgsections()) do
1111 AbstractSection.parse_dynamic(self, k)
1112 if self.map:submitstate() then
1113 Node.parse(self, k, novld)
1115 if not novld and not self.override_scheme and self.map.scheme then
1116 _uvl_validate_section(self, k)
1119 AbstractSection.parse_optionals(self, k)
1122 if self.addremove then
1125 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1126 local name = self.map:formvalue(crval)
1127 if self.anonymous then
1129 created = self:create()
1133 -- Ignore if it already exists
1134 if self:cfgvalue(name) then
1138 name = self:checkscope(name)
1141 self.err_invalid = true
1144 if name and #name > 0 then
1145 created = self:create(name) and name
1147 self.invalid_cts = true
1154 AbstractSection.parse_optionals(self, created)
1158 if created or self.changed then
1163 -- Verifies scope of sections
1164 function TypedSection.checkscope(self, section)
1165 -- Check if we are not excluded
1166 if self.filter and not self:filter(section) then
1170 -- Check if at least one dependency is met
1171 if #self.deps > 0 and self:cfgvalue(section) then
1174 for k, v in ipairs(self.deps) do
1175 if self:cfgvalue(section)[v.option] == v.value then
1185 return self:validate(section)
1189 -- Dummy validate function
1190 function TypedSection.validate(self, section)
1196 AbstractValue - An abstract Value Type
1197 null: Value can be empty
1198 valid: A function returning the value if it is valid otherwise nil
1199 depends: A table of option => value pairs of which one must be true
1200 default: The default value
1201 size: The size of the input fields
1202 rmempty: Unset value if empty
1203 optional: This value is optional (see AbstractSection.optionals)
1205 AbstractValue = class(Node)
1207 function AbstractValue.__init__(self, map, section, option, ...)
1208 Node.__init__(self, ...)
1209 self.section = section
1210 self.option = option
1212 self.config = map.config
1213 self.tag_invalid = {}
1214 self.tag_missing = {}
1215 self.tag_reqerror = {}
1218 --self.cast = "string"
1220 self.track_missing = false
1224 self.optional = false
1227 function AbstractValue.prepare(self)
1228 -- Use defaults from UVL
1229 if not self.override_scheme
1230 and self.map:get_scheme(self.section.sectiontype, self.option) then
1231 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1232 if self.cast == nil then
1233 self.cast = (vs.type == "list") and "list" or "string"
1235 self.title = self.title or vs.title
1236 self.description = self.description or vs.descr
1237 if self.default == nil then
1238 self.default = vs.default
1241 if vs.depends and not self.override_dependencies then
1242 for i, deps in ipairs(vs.depends) do
1243 deps = _uvl_strip_remote_dependencies(deps)
1251 self.cast = self.cast or "string"
1254 -- Add a dependencie to another section field
1255 function AbstractValue.depends(self, field, value)
1257 if type(field) == "string" then
1264 table.insert(self.deps, {deps=deps, add=""})
1267 -- Generates the unique CBID
1268 function AbstractValue.cbid(self, section)
1269 return "cbid."..self.map.config.."."..section.."."..self.option
1272 -- Return whether this object should be created
1273 function AbstractValue.formcreated(self, section)
1274 local key = "cbi.opt."..self.config.."."..section
1275 return (self.map:formvalue(key) == self.option)
1278 -- Returns the formvalue for this object
1279 function AbstractValue.formvalue(self, section)
1280 return self.map:formvalue(self:cbid(section))
1283 function AbstractValue.additional(self, value)
1284 self.optional = value
1287 function AbstractValue.mandatory(self, value)
1288 self.rmempty = not value
1291 function AbstractValue.parse(self, section, novld)
1292 local fvalue = self:formvalue(section)
1293 local cvalue = self:cfgvalue(section)
1295 -- If favlue and cvalue are both tables and have the same content
1296 -- make them identical
1297 if type(fvalue) == "table" and type(cvalue) == "table" then
1298 local equal = #fvalue == #cvalue
1301 if cvalue[i] ~= fvalue[i] then
1311 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1312 fvalue = self:transform(self:validate(fvalue, section))
1313 if not fvalue and not novld then
1315 self.error[section] = "invalid"
1317 self.error = { [section] = "invalid" }
1319 if self.section.error then
1320 table.insert(self.section.error[section], "invalid")
1322 self.section.error = {[section] = {"invalid"}}
1324 self.map.save = false
1326 if fvalue and not (fvalue == cvalue) then
1327 if self:write(section, fvalue) then
1329 self.section.changed = true
1330 --luci.util.append(self.map.events, self.events)
1333 else -- Unset the UCI or error
1334 if self.rmempty or self.optional then
1335 if self:remove(section) then
1337 self.section.changed = true
1338 --luci.util.append(self.map.events, self.events)
1340 elseif cvalue ~= fvalue and not novld then
1341 self:write(section, fvalue or "")
1343 self.error[section] = "missing"
1345 self.error = { [section] = "missing" }
1347 self.map.save = false
1352 -- Render if this value exists or if it is mandatory
1353 function AbstractValue.render(self, s, scope)
1354 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1357 scope.cbid = self:cbid(s)
1358 scope.striptags = luci.util.striptags
1360 scope.ifattr = function(cond,key,val)
1362 return string.format(
1363 ' %s="%s"', tostring(key),
1364 luci.util.pcdata(tostring( val
1366 or (type(self[key]) ~= "function" and self[key])
1374 scope.attr = function(...)
1375 return scope.ifattr( true, ... )
1378 Node.render(self, scope)
1382 -- Return the UCI value of this object
1383 function AbstractValue.cfgvalue(self, section)
1384 local value = self.map:get(section, self.option)
1387 elseif not self.cast or self.cast == type(value) then
1389 elseif self.cast == "string" then
1390 if type(value) == "table" then
1393 elseif self.cast == "table" then
1394 return luci.util.split(value, "%s+", nil, true)
1398 -- Validate the form value
1399 function AbstractValue.validate(self, value)
1403 AbstractValue.transform = AbstractValue.validate
1407 function AbstractValue.write(self, section, value)
1408 return self.map:set(section, self.option, value)
1412 function AbstractValue.remove(self, section)
1413 return self.map:del(section, self.option)
1420 Value - A one-line value
1421 maxlength: The maximum length
1423 Value = class(AbstractValue)
1425 function Value.__init__(self, ...)
1426 AbstractValue.__init__(self, ...)
1427 self.template = "cbi/value"
1432 function Value.value(self, key, val)
1434 table.insert(self.keylist, tostring(key))
1435 table.insert(self.vallist, tostring(val))
1439 -- DummyValue - This does nothing except being there
1440 DummyValue = class(AbstractValue)
1442 function DummyValue.__init__(self, ...)
1443 AbstractValue.__init__(self, ...)
1444 self.template = "cbi/dvalue"
1448 function DummyValue.cfgvalue(self, section)
1451 if type(self.value) == "function" then
1452 value = self:value(section)
1457 value = AbstractValue.cfgvalue(self, section)
1462 function DummyValue.parse(self)
1468 Flag - A flag being enabled or disabled
1470 Flag = class(AbstractValue)
1472 function Flag.__init__(self, ...)
1473 AbstractValue.__init__(self, ...)
1474 self.template = "cbi/fvalue"
1480 -- A flag can only have two states: set or unset
1481 function Flag.parse(self, section)
1482 local fvalue = self:formvalue(section)
1485 fvalue = self.enabled
1487 fvalue = self.disabled
1490 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1491 if not(fvalue == self:cfgvalue(section)) then
1492 self:write(section, fvalue)
1495 self:remove(section)
1502 ListValue - A one-line value predefined in a list
1503 widget: The widget that will be used (select, radio)
1505 ListValue = class(AbstractValue)
1507 function ListValue.__init__(self, ...)
1508 AbstractValue.__init__(self, ...)
1509 self.template = "cbi/lvalue"
1514 self.widget = "select"
1517 function ListValue.prepare(self, ...)
1518 AbstractValue.prepare(self, ...)
1519 if not self.override_scheme
1520 and self.map:get_scheme(self.section.sectiontype, self.option) then
1521 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1522 if self.value and vs.valuelist and not self.override_values then
1523 for k, v in ipairs(vs.valuelist) do
1525 if not self.override_dependencies
1526 and vs.enum_depends and vs.enum_depends[v.value] then
1527 for i, dep in ipairs(vs.enum_depends[v.value]) do
1528 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1531 self:value(v.value, v.title or v.value, unpack(deps))
1537 function ListValue.value(self, key, val, ...)
1538 if luci.util.contains(self.keylist, key) then
1543 table.insert(self.keylist, tostring(key))
1544 table.insert(self.vallist, tostring(val))
1546 for i, deps in ipairs({...}) do
1547 table.insert(self.deps, {add = "-"..key, deps=deps})
1551 function ListValue.validate(self, val)
1552 if luci.util.contains(self.keylist, val) then
1562 MultiValue - Multiple delimited values
1563 widget: The widget that will be used (select, checkbox)
1564 delimiter: The delimiter that will separate the values (default: " ")
1566 MultiValue = class(AbstractValue)
1568 function MultiValue.__init__(self, ...)
1569 AbstractValue.__init__(self, ...)
1570 self.template = "cbi/mvalue"
1575 self.widget = "checkbox"
1576 self.delimiter = " "
1579 function MultiValue.render(self, ...)
1580 if self.widget == "select" and not self.size then
1581 self.size = #self.vallist
1584 AbstractValue.render(self, ...)
1587 function MultiValue.value(self, key, val)
1588 if luci.util.contains(self.keylist, key) then
1593 table.insert(self.keylist, tostring(key))
1594 table.insert(self.vallist, tostring(val))
1597 function MultiValue.valuelist(self, section)
1598 local val = self:cfgvalue(section)
1600 if not(type(val) == "string") then
1604 return luci.util.split(val, self.delimiter)
1607 function MultiValue.validate(self, val)
1608 val = (type(val) == "table") and val or {val}
1612 for i, value in ipairs(val) do
1613 if luci.util.contains(self.keylist, value) then
1614 result = result and (result .. self.delimiter .. value) or value
1622 StaticList = class(MultiValue)
1624 function StaticList.__init__(self, ...)
1625 MultiValue.__init__(self, ...)
1627 self.valuelist = self.cfgvalue
1629 if not self.override_scheme
1630 and self.map:get_scheme(self.section.sectiontype, self.option) then
1631 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1632 if self.value and vs.values and not self.override_values then
1633 for k, v in pairs(vs.values) do
1640 function StaticList.validate(self, value)
1641 value = (type(value) == "table") and value or {value}
1644 for i, v in ipairs(value) do
1645 if luci.util.contains(self.keylist, v) then
1646 table.insert(valid, v)
1653 DynamicList = class(AbstractValue)
1655 function DynamicList.__init__(self, ...)
1656 AbstractValue.__init__(self, ...)
1657 self.template = "cbi/dynlist"
1663 function DynamicList.value(self, key, val)
1665 table.insert(self.keylist, tostring(key))
1666 table.insert(self.vallist, tostring(val))
1669 function DynamicList.write(self, ...)
1670 self.map.proceed = true
1671 return AbstractValue.write(self, ...)
1674 function DynamicList.formvalue(self, section)
1675 local value = AbstractValue.formvalue(self, section)
1676 value = (type(value) == "table") and value or {value}
1679 for i, v in ipairs(value) do
1681 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1682 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1683 table.insert(valid, v)
1692 TextValue - A multi-line value
1695 TextValue = class(AbstractValue)
1697 function TextValue.__init__(self, ...)
1698 AbstractValue.__init__(self, ...)
1699 self.template = "cbi/tvalue"
1705 Button = class(AbstractValue)
1707 function Button.__init__(self, ...)
1708 AbstractValue.__init__(self, ...)
1709 self.template = "cbi/button"
1710 self.inputstyle = nil
1715 FileUpload = class(AbstractValue)
1717 function FileUpload.__init__(self, ...)
1718 AbstractValue.__init__(self, ...)
1719 self.template = "cbi/upload"
1720 if not self.map.upload_fields then
1721 self.map.upload_fields = { self }
1723 self.map.upload_fields[#self.map.upload_fields+1] = self
1727 function FileUpload.formcreated(self, section)
1728 return AbstractValue.formcreated(self, section) or
1729 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1730 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1733 function FileUpload.cfgvalue(self, section)
1734 local val = AbstractValue.cfgvalue(self, section)
1735 if val and fs.access(val) then
1741 function FileUpload.formvalue(self, section)
1742 local val = AbstractValue.formvalue(self, section)
1744 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1745 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1755 function FileUpload.remove(self, section)
1756 local val = AbstractValue.formvalue(self, section)
1757 if val and fs.access(val) then fs.unlink(val) end
1758 return AbstractValue.remove(self, section)
1762 FileBrowser = class(AbstractValue)
1764 function FileBrowser.__init__(self, ...)
1765 AbstractValue.__init__(self, ...)
1766 self.template = "cbi/browser"