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 fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1296 fvalue = self:transform(self:validate(fvalue, section))
1297 if not fvalue and not novld then
1299 self.error[section] = "invalid"
1301 self.error = { [section] = "invalid" }
1303 if self.section.error then
1304 table.insert(self.section.error[section], "invalid")
1306 self.section.error = {[section] = {"invalid"}}
1308 self.map.save = false
1310 if fvalue and not (fvalue == cvalue) then
1311 if self:write(section, fvalue) then
1313 self.section.changed = true
1314 --luci.util.append(self.map.events, self.events)
1317 else -- Unset the UCI or error
1318 if self.rmempty or self.optional then
1319 if self:remove(section) then
1321 self.section.changed = true
1322 --luci.util.append(self.map.events, self.events)
1324 elseif cvalue ~= fvalue and not novld then
1325 self:write(section, fvalue or "")
1327 self.error[section] = "missing"
1329 self.error = { [section] = "missing" }
1331 self.map.save = false
1336 -- Render if this value exists or if it is mandatory
1337 function AbstractValue.render(self, s, scope)
1338 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1341 scope.cbid = self:cbid(s)
1342 scope.striptags = luci.util.striptags
1343 scope.pcdata = luci.util.pcdata
1345 scope.ifattr = function(cond,key,val)
1347 return string.format(
1348 ' %s="%s"', tostring(key),
1349 luci.util.pcdata(tostring( val
1351 or (type(self[key]) ~= "function" and self[key])
1359 scope.attr = function(...)
1360 return scope.ifattr( true, ... )
1363 Node.render(self, scope)
1367 -- Return the UCI value of this object
1368 function AbstractValue.cfgvalue(self, section)
1369 local value = self.map:get(section, self.option)
1372 elseif not self.cast or self.cast == type(value) then
1374 elseif self.cast == "string" then
1375 if type(value) == "table" then
1378 elseif self.cast == "table" then
1379 return luci.util.split(value, "%s+", nil, true)
1383 -- Validate the form value
1384 function AbstractValue.validate(self, value)
1388 AbstractValue.transform = AbstractValue.validate
1392 function AbstractValue.write(self, section, value)
1393 return self.map:set(section, self.option, value)
1397 function AbstractValue.remove(self, section)
1398 return self.map:del(section, self.option)
1405 Value - A one-line value
1406 maxlength: The maximum length
1408 Value = class(AbstractValue)
1410 function Value.__init__(self, ...)
1411 AbstractValue.__init__(self, ...)
1412 self.template = "cbi/value"
1417 function Value.value(self, key, val)
1419 table.insert(self.keylist, tostring(key))
1420 table.insert(self.vallist, tostring(val))
1424 -- DummyValue - This does nothing except being there
1425 DummyValue = class(AbstractValue)
1427 function DummyValue.__init__(self, ...)
1428 AbstractValue.__init__(self, ...)
1429 self.template = "cbi/dvalue"
1433 function DummyValue.cfgvalue(self, section)
1436 if type(self.value) == "function" then
1437 value = self:value(section)
1442 value = AbstractValue.cfgvalue(self, section)
1447 function DummyValue.parse(self)
1453 Flag - A flag being enabled or disabled
1455 Flag = class(AbstractValue)
1457 function Flag.__init__(self, ...)
1458 AbstractValue.__init__(self, ...)
1459 self.template = "cbi/fvalue"
1465 -- A flag can only have two states: set or unset
1466 function Flag.parse(self, section)
1467 local fvalue = self:formvalue(section)
1470 fvalue = self.enabled
1472 fvalue = self.disabled
1475 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1476 if not(fvalue == self:cfgvalue(section)) then
1477 self:write(section, fvalue)
1480 self:remove(section)
1487 ListValue - A one-line value predefined in a list
1488 widget: The widget that will be used (select, radio)
1490 ListValue = class(AbstractValue)
1492 function ListValue.__init__(self, ...)
1493 AbstractValue.__init__(self, ...)
1494 self.template = "cbi/lvalue"
1499 self.widget = "select"
1502 function ListValue.prepare(self, ...)
1503 AbstractValue.prepare(self, ...)
1504 if not self.override_scheme
1505 and self.map:get_scheme(self.section.sectiontype, self.option) then
1506 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1507 if self.value and vs.valuelist and not self.override_values then
1508 for k, v in ipairs(vs.valuelist) do
1510 if not self.override_dependencies
1511 and vs.enum_depends and vs.enum_depends[v.value] then
1512 for i, dep in ipairs(vs.enum_depends[v.value]) do
1513 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1516 self:value(v.value, v.title or v.value, unpack(deps))
1522 function ListValue.value(self, key, val, ...)
1523 if luci.util.contains(self.keylist, key) then
1528 table.insert(self.keylist, tostring(key))
1529 table.insert(self.vallist, tostring(val))
1531 for i, deps in ipairs({...}) do
1532 table.insert(self.deps, {add = "-"..key, deps=deps})
1536 function ListValue.validate(self, val)
1537 if luci.util.contains(self.keylist, val) then
1547 MultiValue - Multiple delimited values
1548 widget: The widget that will be used (select, checkbox)
1549 delimiter: The delimiter that will separate the values (default: " ")
1551 MultiValue = class(AbstractValue)
1553 function MultiValue.__init__(self, ...)
1554 AbstractValue.__init__(self, ...)
1555 self.template = "cbi/mvalue"
1560 self.widget = "checkbox"
1561 self.delimiter = " "
1564 function MultiValue.render(self, ...)
1565 if self.widget == "select" and not self.size then
1566 self.size = #self.vallist
1569 AbstractValue.render(self, ...)
1572 function MultiValue.value(self, key, val)
1573 if luci.util.contains(self.keylist, key) then
1578 table.insert(self.keylist, tostring(key))
1579 table.insert(self.vallist, tostring(val))
1582 function MultiValue.valuelist(self, section)
1583 local val = self:cfgvalue(section)
1585 if not(type(val) == "string") then
1589 return luci.util.split(val, self.delimiter)
1592 function MultiValue.validate(self, val)
1593 val = (type(val) == "table") and val or {val}
1597 for i, value in ipairs(val) do
1598 if luci.util.contains(self.keylist, value) then
1599 result = result and (result .. self.delimiter .. value) or value
1607 StaticList = class(MultiValue)
1609 function StaticList.__init__(self, ...)
1610 MultiValue.__init__(self, ...)
1612 self.valuelist = self.cfgvalue
1614 if not self.override_scheme
1615 and self.map:get_scheme(self.section.sectiontype, self.option) then
1616 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1617 if self.value and vs.values and not self.override_values then
1618 for k, v in pairs(vs.values) do
1625 function StaticList.validate(self, value)
1626 value = (type(value) == "table") and value or {value}
1629 for i, v in ipairs(value) do
1630 if luci.util.contains(self.keylist, v) then
1631 table.insert(valid, v)
1638 DynamicList = class(AbstractValue)
1640 function DynamicList.__init__(self, ...)
1641 AbstractValue.__init__(self, ...)
1642 self.template = "cbi/dynlist"
1648 function DynamicList.value(self, key, val)
1650 table.insert(self.keylist, tostring(key))
1651 table.insert(self.vallist, tostring(val))
1654 function DynamicList.write(self, ...)
1655 self.map.proceed = true
1656 return AbstractValue.write(self, ...)
1659 function DynamicList.formvalue(self, section)
1660 local value = AbstractValue.formvalue(self, section)
1661 value = (type(value) == "table") and value or {value}
1664 for i, v in ipairs(value) do
1666 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1667 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1668 table.insert(valid, v)
1677 TextValue - A multi-line value
1680 TextValue = class(AbstractValue)
1682 function TextValue.__init__(self, ...)
1683 AbstractValue.__init__(self, ...)
1684 self.template = "cbi/tvalue"
1690 Button = class(AbstractValue)
1692 function Button.__init__(self, ...)
1693 AbstractValue.__init__(self, ...)
1694 self.template = "cbi/button"
1695 self.inputstyle = nil
1700 FileUpload = class(AbstractValue)
1702 function FileUpload.__init__(self, ...)
1703 AbstractValue.__init__(self, ...)
1704 self.template = "cbi/upload"
1705 if not self.map.upload_fields then
1706 self.map.upload_fields = { self }
1708 self.map.upload_fields[#self.map.upload_fields+1] = self
1712 function FileUpload.formcreated(self, section)
1713 return AbstractValue.formcreated(self, section) or
1714 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1715 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1718 function FileUpload.cfgvalue(self, section)
1719 local val = AbstractValue.cfgvalue(self, section)
1720 if val and fs.access(val) then
1726 function FileUpload.formvalue(self, section)
1727 local val = AbstractValue.formvalue(self, section)
1729 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1730 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1740 function FileUpload.remove(self, section)
1741 local val = AbstractValue.formvalue(self, section)
1742 if val and fs.access(val) then fs.unlink(val) end
1743 return AbstractValue.remove(self, section)
1747 FileBrowser = class(AbstractValue)
1749 function FileBrowser.__init__(self, ...)
1750 AbstractValue.__init__(self, ...)
1751 self.template = "cbi/browser"