1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Licensed to the public under the Apache License 2.0.
4 module("luci.cbi", package.seeall)
6 require("luci.template")
7 local util = require("luci.util")
11 --local event = require "luci.sys.event"
12 local fs = require("nixio.fs")
13 local uci = require("luci.model.uci")
14 local datatypes = require("luci.cbi.datatypes")
15 local dispatcher = require("luci.dispatcher")
16 local class = util.class
17 local instanceof = util.instanceof
29 CREATE_PREFIX = "cbi.cts."
30 REMOVE_PREFIX = "cbi.rts."
31 RESORT_PREFIX = "cbi.sts."
32 FEXIST_PREFIX = "cbi.cbe."
34 -- Loads a CBI map from given file, creating an environment and returns it
35 function load(cbimap, ...)
36 local fs = require "nixio.fs"
37 local i18n = require "luci.i18n"
38 require("luci.config")
41 local upldir = "/etc/luci-uploads/"
42 local cbidir = luci.util.libpath() .. "/model/cbi/"
45 if fs.access(cbidir..cbimap..".lua") then
46 func, err = loadfile(cbidir..cbimap..".lua")
47 elseif fs.access(cbimap) then
48 func, err = loadfile(cbimap)
50 func, err = nil, "Model '" .. cbimap .. "' not found!"
56 translate=i18n.translate,
57 translatef=i18n.translatef,
61 setfenv(func, setmetatable(env, {__index =
63 return rawget(tbl, key) or _M[key] or _G[key]
66 local maps = { func() }
68 local has_upload = false
70 for i, map in ipairs(maps) do
71 if not instanceof(map, Node) then
72 error("CBI map returns no valid map object!")
76 if map.upload_fields then
78 for _, field in ipairs(map.upload_fields) do
80 field.config .. '.' ..
81 (field.section.sectiontype or '1') .. '.' ..
90 local uci = luci.model.uci.cursor()
91 local prm = luci.http.context.request.message.params
94 luci.http.setfilehandler(
95 function( field, chunk, eof )
96 if not field then return end
97 if field.name and not cbid then
98 local c, s, o = field.name:gmatch(
99 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
102 if c and s and o then
103 local t = uci:get( c, s ) or s
104 if uploads[c.."."..t.."."..o] then
105 local path = upldir .. field.name
106 fd = io.open(path, "w")
115 if field.name == cbid and fd then
132 -- Compile a datatype specification into a parse tree for evaluation later on
134 local cdt_cache = { }
136 function compile_datatype(code)
143 for i = 1, #code+1 do
144 local byte = code:byte(i) or 44
147 elseif byte == 92 then
149 elseif byte == 40 or byte == 44 then
152 local label = code:sub(pos, i-1)
157 if #label > 0 and tonumber(label) then
158 stack[#stack+1] = tonumber(label)
159 elseif label:match("^'.*'$") or label:match('^".*"$') then
160 stack[#stack+1] = label:gsub("[\"'](.*)[\"']", "%1")
161 elseif type(datatypes[label]) == "function" then
162 stack[#stack+1] = datatypes[label]
163 stack[#stack+1] = { }
165 error("Datatype error, bad token %q" % label)
170 depth = depth + (byte == 40 and 1 or 0)
171 elseif byte == 41 then
174 if type(stack[#stack-1]) ~= "function" then
175 error("Datatype error, argument list follows non-function")
177 stack[#stack] = compile_datatype(code:sub(pos, i-1))
186 function verify_datatype(dt, value)
187 if dt and #dt > 0 then
188 if not cdt_cache[dt] then
189 local c = compile_datatype(dt)
190 if c and type(c[1]) == "function" then
193 error("Datatype error, not a function expression")
196 if cdt_cache[dt] then
197 return cdt_cache[dt][1](value, unpack(cdt_cache[dt][2]))
204 -- Node pseudo abstract class
207 function Node.__init__(self, title, description)
209 self.title = title or ""
210 self.description = description or ""
211 self.template = "cbi/node"
215 function Node._run_hook(self, hook)
216 if type(self[hook]) == "function" then
217 return self[hook](self)
221 function Node._run_hooks(self, ...)
224 for _, f in ipairs(arg) do
225 if type(self[f]) == "function" then
234 function Node.prepare(self, ...)
235 for k, child in ipairs(self.children) do
240 -- Append child nodes
241 function Node.append(self, obj)
242 table.insert(self.children, obj)
245 -- Parse this node and its children
246 function Node.parse(self, ...)
247 for k, child in ipairs(self.children) do
253 function Node.render(self, scope)
257 luci.template.render(self.template, scope)
260 -- Render the children
261 function Node.render_children(self, ...)
263 for k, node in ipairs(self.children) do
264 node.last_child = (k == #self.children)
272 A simple template element
274 Template = class(Node)
276 function Template.__init__(self, template)
278 self.template = template
281 function Template.render(self)
282 luci.template.render(self.template, {self=self})
285 function Template.parse(self, readinput)
286 self.readinput = (readinput ~= false)
287 return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
292 Map - A map describing a configuration file
296 function Map.__init__(self, config, ...)
297 Node.__init__(self, ...)
300 self.parsechain = {self.config}
301 self.template = "cbi/map"
302 self.apply_on_parse = nil
303 self.readinput = true
307 self.uci = uci.cursor()
312 local path = "%s/%s" %{ self.uci:get_confdir(), self.config }
313 if fs.stat(path, "type") ~= "reg" then
314 fs.writefile(path, "")
317 local ok, err = self.uci:load(self.config)
319 local url = dispatcher.build_url(unpack(dispatcher.context.request))
320 local source = self:formvalue("cbi.source")
321 if type(source) == "string" then
322 fs.writefile(path, source:gsub("\r\n", "\n"))
323 ok, err = self.uci:load(self.config)
325 luci.http.redirect(url)
332 self.template = "cbi/error"
334 self.source = fs.readfile(path) or ""
335 self.pageaction = false
339 function Map.formvalue(self, key)
340 return self.readinput and luci.http.formvalue(key) or nil
343 function Map.formvaluetable(self, key)
344 return self.readinput and luci.http.formvaluetable(key) or {}
347 function Map.get_scheme(self, sectiontype, option)
349 return self.scheme and self.scheme.sections[sectiontype]
351 return self.scheme and self.scheme.variables[sectiontype]
352 and self.scheme.variables[sectiontype][option]
356 function Map.submitstate(self)
357 return self:formvalue("cbi.submit")
360 -- Chain foreign config
361 function Map.chain(self, config)
362 table.insert(self.parsechain, config)
365 function Map.state_handler(self, state)
369 -- Use optimized UCI writing
370 function Map.parse(self, readinput, ...)
371 if self:formvalue("cbi.skip") then
372 self.state = FORM_SKIP
373 elseif not self.save then
374 self.state = FORM_INVALID
375 elseif not self:submitstate() then
376 self.state = FORM_NODATA
379 -- Back out early to prevent unauthorized changes on the subsequent parse
380 if self.state ~= nil then
381 return self:state_handler(self.state)
384 self.readinput = (readinput ~= false)
385 self:_run_hooks("on_parse")
387 Node.parse(self, ...)
390 self:_run_hooks("on_save", "on_before_save")
391 for i, config in ipairs(self.parsechain) do
392 self.uci:save(config)
394 self:_run_hooks("on_after_save")
395 if (not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply") then
396 self:_run_hooks("on_before_commit")
397 for i, config in ipairs(self.parsechain) do
398 self.uci:commit(config)
400 -- Refresh data because commit changes section names
401 self.uci:load(config)
403 self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
404 if self.apply_on_parse then
405 self.uci:apply(self.parsechain)
406 self:_run_hooks("on_apply", "on_after_apply")
408 -- This is evaluated by the dispatcher and delegated to the
409 -- template which in turn fires XHR to perform the actual
411 self.apply_needed = true
415 Node.parse(self, true)
417 for i, config in ipairs(self.parsechain) do
418 self.uci:unload(config)
420 if type(self.commit_handler) == "function" then
421 self:commit_handler(self:submitstate())
425 if not self.save then
426 self.state = FORM_INVALID
427 elseif self.proceed then
428 self.state = FORM_PROCEED
429 elseif self.changed then
430 self.state = FORM_CHANGED
432 self.state = FORM_VALID
435 return self:state_handler(self.state)
438 function Map.render(self, ...)
439 self:_run_hooks("on_init")
440 Node.render(self, ...)
443 -- Creates a child section
444 function Map.section(self, class, ...)
445 if instanceof(class, AbstractSection) then
446 local obj = class(self, ...)
450 error("class must be a descendent of AbstractSection")
455 function Map.add(self, sectiontype)
456 return self.uci:add(self.config, sectiontype)
460 function Map.set(self, section, option, value)
461 if type(value) ~= "table" or #value > 0 then
463 return self.uci:set(self.config, section, option, value)
465 return self.uci:set(self.config, section, value)
468 return Map.del(self, section, option)
473 function Map.del(self, section, option)
475 return self.uci:delete(self.config, section, option)
477 return self.uci:delete(self.config, section)
482 function Map.get(self, section, option)
484 return self.uci:get_all(self.config)
486 return self.uci:get(self.config, section, option)
488 return self.uci:get_all(self.config, section)
495 Compound = class(Node)
497 function Compound.__init__(self, ...)
499 self.template = "cbi/compound"
500 self.children = {...}
503 function Compound.populate_delegator(self, delegator)
504 for _, v in ipairs(self.children) do
505 v.delegator = delegator
509 function Compound.parse(self, ...)
510 local cstate, state = 0
512 for k, child in ipairs(self.children) do
513 cstate = child:parse(...)
514 state = (not state or cstate < state) and cstate or state
522 Delegator - Node controller
524 Delegator = class(Node)
525 function Delegator.__init__(self, ...)
526 Node.__init__(self, ...)
528 self.defaultpath = {}
529 self.pageaction = false
530 self.readinput = true
531 self.allow_reset = false
532 self.allow_cancel = false
533 self.allow_back = false
534 self.allow_finish = false
535 self.template = "cbi/delegator"
538 function Delegator.set(self, name, node)
539 assert(not self.nodes[name], "Duplicate entry")
541 self.nodes[name] = node
544 function Delegator.add(self, name, node)
545 node = self:set(name, node)
546 self.defaultpath[#self.defaultpath+1] = name
549 function Delegator.insert_after(self, name, after)
550 local n = #self.chain + 1
551 for k, v in ipairs(self.chain) do
557 table.insert(self.chain, n, name)
560 function Delegator.set_route(self, ...)
561 local n, chain, route = 0, self.chain, {...}
563 if chain[i] == self.current then
572 for i = n + 1, #chain do
577 function Delegator.get(self, name)
578 local node = self.nodes[name]
580 if type(node) == "string" then
581 node = load(node, name)
584 if type(node) == "table" and getmetatable(node) == nil then
585 node = Compound(unpack(node))
591 function Delegator.parse(self, ...)
592 if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
593 if self:_run_hooks("on_cancel") then
598 if not Map.formvalue(self, "cbi.delg.current") then
599 self:_run_hooks("on_init")
603 self.chain = self.chain or self:get_chain()
604 self.current = self.current or self:get_active()
605 self.active = self.active or self:get(self.current)
606 assert(self.active, "Invalid state")
608 local stat = FORM_DONE
609 if type(self.active) ~= "function" then
610 self.active:populate_delegator(self)
611 stat = self.active:parse()
616 if stat > FORM_PROCEED then
617 if Map.formvalue(self, "cbi.delg.back") then
618 newcurrent = self:get_prev(self.current)
620 newcurrent = self:get_next(self.current)
622 elseif stat < FORM_PROCEED then
627 if not Map.formvalue(self, "cbi.submit") then
629 elseif stat > FORM_PROCEED
630 and (not newcurrent or not self:get(newcurrent)) then
631 return self:_run_hook("on_done") or FORM_DONE
633 self.current = newcurrent or self.current
634 self.active = self:get(self.current)
635 if type(self.active) ~= "function" then
636 self.active:populate_delegator(self)
637 local stat = self.active:parse(false)
638 if stat == FORM_SKIP then
639 return self:parse(...)
644 return self:parse(...)
649 function Delegator.get_next(self, state)
650 for k, v in ipairs(self.chain) do
652 return self.chain[k+1]
657 function Delegator.get_prev(self, state)
658 for k, v in ipairs(self.chain) do
660 return self.chain[k-1]
665 function Delegator.get_chain(self)
666 local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
667 return type(x) == "table" and x or {x}
670 function Delegator.get_active(self)
671 return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
679 Page.__init__ = Node.__init__
680 Page.parse = function() end
684 SimpleForm - A Simple non-UCI form
686 SimpleForm = class(Node)
688 function SimpleForm.__init__(self, config, title, description, data)
689 Node.__init__(self, title, description)
691 self.data = data or {}
692 self.template = "cbi/simpleform"
694 self.pageaction = false
695 self.readinput = true
698 SimpleForm.formvalue = Map.formvalue
699 SimpleForm.formvaluetable = Map.formvaluetable
701 function SimpleForm.parse(self, readinput, ...)
702 self.readinput = (readinput ~= false)
704 if self:formvalue("cbi.skip") then
708 if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
712 if self:submitstate() then
713 Node.parse(self, 1, ...)
717 for k, j in ipairs(self.children) do
718 for i, v in ipairs(j.children) do
720 and (not v.tag_missing or not v.tag_missing[1])
721 and (not v.tag_invalid or not v.tag_invalid[1])
727 not self:submitstate() and FORM_NODATA
728 or valid and FORM_VALID
731 self.dorender = not self.handle
733 local nrender, nstate = self:handle(state, self.data)
734 self.dorender = self.dorender or (nrender ~= false)
735 state = nstate or state
740 function SimpleForm.render(self, ...)
741 if self.dorender then
742 Node.render(self, ...)
746 function SimpleForm.submitstate(self)
747 return self:formvalue("cbi.submit")
750 function SimpleForm.section(self, class, ...)
751 if instanceof(class, AbstractSection) then
752 local obj = class(self, ...)
756 error("class must be a descendent of AbstractSection")
760 -- Creates a child field
761 function SimpleForm.field(self, class, ...)
763 for k, v in ipairs(self.children) do
764 if instanceof(v, SimpleSection) then
770 section = self:section(SimpleSection)
773 if instanceof(class, AbstractValue) then
774 local obj = class(self, section, ...)
775 obj.track_missing = true
779 error("class must be a descendent of AbstractValue")
783 function SimpleForm.set(self, section, option, value)
784 self.data[option] = value
788 function SimpleForm.del(self, section, option)
789 self.data[option] = nil
793 function SimpleForm.get(self, section, option)
794 return self.data[option]
798 function SimpleForm.get_scheme()
803 Form = class(SimpleForm)
805 function Form.__init__(self, ...)
806 SimpleForm.__init__(self, ...)
814 AbstractSection = class(Node)
816 function AbstractSection.__init__(self, map, sectiontype, ...)
817 Node.__init__(self, ...)
818 self.sectiontype = sectiontype
820 self.config = map.config
825 self.tag_invalid = {}
826 self.tag_deperror = {}
830 self.addremove = false
834 -- Define a tab for the section
835 function AbstractSection.tab(self, tab, title, desc)
836 self.tabs = self.tabs or { }
837 self.tab_names = self.tab_names or { }
839 self.tab_names[#self.tab_names+1] = tab
847 -- Check whether the section has tabs
848 function AbstractSection.has_tabs(self)
849 return (self.tabs ~= nil) and (next(self.tabs) ~= nil)
852 -- Appends a new option
853 function AbstractSection.option(self, class, option, ...)
854 if instanceof(class, AbstractValue) then
855 local obj = class(self.map, self, option, ...)
857 self.fields[option] = obj
859 elseif class == true then
860 error("No valid class was given and autodetection failed.")
862 error("class must be a descendant of AbstractValue")
866 -- Appends a new tabbed option
867 function AbstractSection.taboption(self, tab, ...)
869 assert(tab and self.tabs and self.tabs[tab],
870 "Cannot assign option to not existing tab %q" % tostring(tab))
872 local l = self.tabs[tab].childs
873 local o = AbstractSection.option(self, ...)
875 if o then l[#l+1] = o end
880 -- Render a single tab
881 function AbstractSection.render_tab(self, tab, ...)
883 assert(tab and self.tabs and self.tabs[tab],
884 "Cannot render not existing tab %q" % tostring(tab))
887 for k, node in ipairs(self.tabs[tab].childs) do
888 node.last_child = (k == #self.tabs[tab].childs)
894 -- Parse optional options
895 function AbstractSection.parse_optionals(self, section, noparse)
896 if not self.optional then
900 self.optionals[section] = {}
904 field = self.map:formvalue("cbi.opt."..self.config.."."..section)
907 for k,v in ipairs(self.children) do
908 if v.optional and not v:cfgvalue(section) and not self:has_tabs() then
909 if field == v.option then
911 self.map.proceed = true
913 table.insert(self.optionals[section], v)
918 if field and #field > 0 and self.dynamic then
919 self:add_dynamic(field)
923 -- Add a dynamic option
924 function AbstractSection.add_dynamic(self, field, optional)
925 local o = self:option(Value, field, field)
926 o.optional = optional
929 -- Parse all dynamic options
930 function AbstractSection.parse_dynamic(self, section)
931 if not self.dynamic then
935 local arr = luci.util.clone(self:cfgvalue(section))
936 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
937 for k, v in pairs(form) do
941 for key,val in pairs(arr) do
944 for i,c in ipairs(self.children) do
945 if c.option == key then
950 if create and key:sub(1, 1) ~= "." then
951 self.map.proceed = true
952 self:add_dynamic(key, true)
957 -- Returns the section's UCI table
958 function AbstractSection.cfgvalue(self, section)
959 return self.map:get(section)
963 function AbstractSection.push_events(self)
964 --luci.util.append(self.map.events, self.events)
965 self.map.changed = true
968 -- Removes the section
969 function AbstractSection.remove(self, section)
970 self.map.proceed = true
971 return self.map:del(section)
974 -- Creates the section
975 function AbstractSection.create(self, section)
979 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
981 section = self.map:add(self.sectiontype)
986 for k,v in pairs(self.children) do
988 self.map:set(section, v.option, v.default)
992 for k,v in pairs(self.defaults) do
993 self.map:set(section, k, v)
997 self.map.proceed = true
1003 SimpleSection = class(AbstractSection)
1005 function SimpleSection.__init__(self, form, ...)
1006 AbstractSection.__init__(self, form, nil, ...)
1007 self.template = "cbi/nullsection"
1011 Table = class(AbstractSection)
1013 function Table.__init__(self, form, data, ...)
1014 local datasource = {}
1016 datasource.config = "table"
1017 self.data = data or {}
1019 datasource.formvalue = Map.formvalue
1020 datasource.formvaluetable = Map.formvaluetable
1021 datasource.readinput = true
1023 function datasource.get(self, section, option)
1024 return tself.data[section] and tself.data[section][option]
1027 function datasource.submitstate(self)
1028 return Map.formvalue(self, "cbi.submit")
1031 function datasource.del(...)
1035 function datasource.get_scheme()
1039 AbstractSection.__init__(self, datasource, "table", ...)
1040 self.template = "cbi/tblsection"
1041 self.rowcolors = true
1042 self.anonymous = true
1045 function Table.parse(self, readinput)
1046 self.map.readinput = (readinput ~= false)
1047 for i, k in ipairs(self:cfgsections()) do
1048 if self.map:submitstate() then
1054 function Table.cfgsections(self)
1057 for i, v in luci.util.kspairs(self.data) do
1058 table.insert(sections, i)
1064 function Table.update(self, data)
1071 NamedSection - A fixed configuration section defined by its name
1073 NamedSection = class(AbstractSection)
1075 function NamedSection.__init__(self, map, section, stype, ...)
1076 AbstractSection.__init__(self, map, stype, ...)
1079 self.addremove = false
1080 self.template = "cbi/nsection"
1081 self.section = section
1084 function NamedSection.prepare(self)
1085 AbstractSection.prepare(self)
1086 AbstractSection.parse_optionals(self, self.section, true)
1089 function NamedSection.parse(self, novld)
1090 local s = self.section
1091 local active = self:cfgvalue(s)
1093 if self.addremove then
1094 local path = self.config.."."..s
1095 if active then -- Remove the section
1096 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1100 else -- Create and apply default values
1101 if self.map:formvalue("cbi.cns."..path) then
1109 AbstractSection.parse_dynamic(self, s)
1110 if self.map:submitstate() then
1113 AbstractSection.parse_optionals(self, s)
1115 if self.changed then
1123 TypedSection - A (set of) configuration section(s) defined by the type
1124 addremove: Defines whether the user can add/remove sections of this type
1125 anonymous: Allow creating anonymous sections
1126 validate: a validation function returning nil if the section is invalid
1128 TypedSection = class(AbstractSection)
1130 function TypedSection.__init__(self, map, type, ...)
1131 AbstractSection.__init__(self, map, type, ...)
1133 self.template = "cbi/tsection"
1135 self.anonymous = false
1138 function TypedSection.prepare(self)
1139 AbstractSection.prepare(self)
1142 for i, s in ipairs(self:cfgsections()) do
1143 AbstractSection.parse_optionals(self, s, true)
1147 -- Return all matching UCI sections for this TypedSection
1148 function TypedSection.cfgsections(self)
1150 self.map.uci:foreach(self.map.config, self.sectiontype,
1152 if self:checkscope(section[".name"]) then
1153 table.insert(sections, section[".name"])
1160 -- Limits scope to sections that have certain option => value pairs
1161 function TypedSection.depends(self, option, value)
1162 table.insert(self.deps, {option=option, value=value})
1165 function TypedSection.parse(self, novld)
1166 if self.addremove then
1168 local crval = REMOVE_PREFIX .. self.config
1169 local name = self.map:formvaluetable(crval)
1170 for k,v in pairs(name) do
1171 if k:sub(-2) == ".x" then
1172 k = k:sub(1, #k - 2)
1174 if self:cfgvalue(k) and self:checkscope(k) then
1181 for i, k in ipairs(self:cfgsections()) do
1182 AbstractSection.parse_dynamic(self, k)
1183 if self.map:submitstate() then
1184 Node.parse(self, k, novld)
1186 AbstractSection.parse_optionals(self, k)
1189 if self.addremove then
1192 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1193 local origin, name = next(self.map:formvaluetable(crval))
1194 if self.anonymous then
1196 created = self:create(nil, origin)
1200 -- Ignore if it already exists
1201 if self:cfgvalue(name) then
1205 name = self:checkscope(name)
1208 self.err_invalid = true
1211 if name and #name > 0 then
1212 created = self:create(name, origin) and name
1214 self.invalid_cts = true
1221 AbstractSection.parse_optionals(self, created)
1225 if self.sortable then
1226 local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype
1227 local order = self.map:formvalue(stval)
1228 if order and #order > 0 then
1231 for sid in util.imatch(order) do
1232 self.map.uci:reorder(self.config, sid, num)
1235 self.changed = (num > 0)
1239 if created or self.changed then
1244 -- Verifies scope of sections
1245 function TypedSection.checkscope(self, section)
1246 -- Check if we are not excluded
1247 if self.filter and not self:filter(section) then
1251 -- Check if at least one dependency is met
1252 if #self.deps > 0 and self:cfgvalue(section) then
1255 for k, v in ipairs(self.deps) do
1256 if self:cfgvalue(section)[v.option] == v.value then
1266 return self:validate(section)
1270 -- Dummy validate function
1271 function TypedSection.validate(self, section)
1277 AbstractValue - An abstract Value Type
1278 null: Value can be empty
1279 valid: A function returning the value if it is valid otherwise nil
1280 depends: A table of option => value pairs of which one must be true
1281 default: The default value
1282 size: The size of the input fields
1283 rmempty: Unset value if empty
1284 optional: This value is optional (see AbstractSection.optionals)
1286 AbstractValue = class(Node)
1288 function AbstractValue.__init__(self, map, section, option, ...)
1289 Node.__init__(self, ...)
1290 self.section = section
1291 self.option = option
1293 self.config = map.config
1294 self.tag_invalid = {}
1295 self.tag_missing = {}
1296 self.tag_reqerror = {}
1299 --self.cast = "string"
1301 self.track_missing = false
1305 self.optional = false
1308 function AbstractValue.prepare(self)
1309 self.cast = self.cast or "string"
1312 -- Add a dependencie to another section field
1313 function AbstractValue.depends(self, field, value)
1315 if type(field) == "string" then
1322 table.insert(self.deps, deps)
1325 -- Serialize dependencies
1326 function AbstractValue.deplist2json(self, section, deplist)
1327 local deps, i, d = { }
1329 if type(self.deps) == "table" then
1330 for i, d in ipairs(deplist or self.deps) do
1332 for k, v in pairs(d) do
1333 if k:find("!", 1, true) then
1335 elseif k:find(".", 1, true) then
1336 a['cbid.%s' % k] = v
1338 a['cbid.%s.%s.%s' %{ self.config, section, k }] = v
1345 return util.serialize_json(deps)
1348 -- Generates the unique CBID
1349 function AbstractValue.cbid(self, section)
1350 return "cbid."..self.map.config.."."..section.."."..self.option
1353 -- Return whether this object should be created
1354 function AbstractValue.formcreated(self, section)
1355 local key = "cbi.opt."..self.config.."."..section
1356 return (self.map:formvalue(key) == self.option)
1359 -- Returns the formvalue for this object
1360 function AbstractValue.formvalue(self, section)
1361 return self.map:formvalue(self:cbid(section))
1364 function AbstractValue.additional(self, value)
1365 self.optional = value
1368 function AbstractValue.mandatory(self, value)
1369 self.rmempty = not value
1372 function AbstractValue.add_error(self, section, type, msg)
1373 self.error = self.error or { }
1374 self.error[section] = msg or type
1376 self.section.error = self.section.error or { }
1377 self.section.error[section] = self.section.error[section] or { }
1378 table.insert(self.section.error[section], msg or type)
1380 if type == "invalid" then
1381 self.tag_invalid[section] = true
1382 elseif type == "missing" then
1383 self.tag_missing[section] = true
1386 self.tag_error[section] = true
1387 self.map.save = false
1390 function AbstractValue.parse(self, section, novld)
1391 local fvalue = self:formvalue(section)
1392 local cvalue = self:cfgvalue(section)
1394 -- If favlue and cvalue are both tables and have the same content
1395 -- make them identical
1396 if type(fvalue) == "table" and type(cvalue) == "table" then
1397 local equal = #fvalue == #cvalue
1400 if cvalue[i] ~= fvalue[i] then
1410 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1412 fvalue, val_err = self:validate(fvalue, section)
1413 fvalue = self:transform(fvalue)
1415 if not fvalue and not novld then
1416 self:add_error(section, "invalid", val_err)
1419 if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
1420 if self:write(section, fvalue) then
1422 self.section.changed = true
1423 --luci.util.append(self.map.events, self.events)
1426 else -- Unset the UCI or error
1427 if self.rmempty or self.optional then
1428 if self:remove(section) then
1430 self.section.changed = true
1431 --luci.util.append(self.map.events, self.events)
1433 elseif cvalue ~= fvalue and not novld then
1434 -- trigger validator with nil value to get custom user error msg.
1435 local _, val_err = self:validate(nil, section)
1436 self:add_error(section, "missing", val_err)
1441 -- Render if this value exists or if it is mandatory
1442 function AbstractValue.render(self, s, scope)
1443 if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
1446 scope.cbid = self:cbid(s)
1447 Node.render(self, scope)
1451 -- Return the UCI value of this object
1452 function AbstractValue.cfgvalue(self, section)
1454 if self.tag_error[section] then
1455 value = self:formvalue(section)
1457 value = self.map:get(section, self.option)
1462 elseif not self.cast or self.cast == type(value) then
1464 elseif self.cast == "string" then
1465 if type(value) == "table" then
1468 elseif self.cast == "table" then
1473 -- Validate the form value
1474 function AbstractValue.validate(self, value)
1475 if self.datatype and value then
1476 if type(value) == "table" then
1478 for _, v in ipairs(value) do
1479 if v and #v > 0 and not verify_datatype(self.datatype, v) then
1484 if not verify_datatype(self.datatype, value) then
1493 AbstractValue.transform = AbstractValue.validate
1497 function AbstractValue.write(self, section, value)
1498 return self.map:set(section, self.option, value)
1502 function AbstractValue.remove(self, section)
1503 return self.map:del(section, self.option)
1510 Value - A one-line value
1511 maxlength: The maximum length
1513 Value = class(AbstractValue)
1515 function Value.__init__(self, ...)
1516 AbstractValue.__init__(self, ...)
1517 self.template = "cbi/value"
1523 function Value.reset_values(self)
1528 function Value.value(self, key, val)
1530 table.insert(self.keylist, tostring(key))
1531 table.insert(self.vallist, tostring(val))
1534 function Value.parse(self, section, novld)
1535 if self.readonly then return end
1536 AbstractValue.parse(self, section, novld)
1539 -- DummyValue - This does nothing except being there
1540 DummyValue = class(AbstractValue)
1542 function DummyValue.__init__(self, ...)
1543 AbstractValue.__init__(self, ...)
1544 self.template = "cbi/dvalue"
1548 function DummyValue.cfgvalue(self, section)
1551 if type(self.value) == "function" then
1552 value = self:value(section)
1557 value = AbstractValue.cfgvalue(self, section)
1562 function DummyValue.parse(self)
1568 Flag - A flag being enabled or disabled
1570 Flag = class(AbstractValue)
1572 function Flag.__init__(self, ...)
1573 AbstractValue.__init__(self, ...)
1574 self.template = "cbi/fvalue"
1578 self.default = self.disabled
1581 -- A flag can only have two states: set or unset
1582 function Flag.parse(self, section, novld)
1583 local fexists = self.map:formvalue(
1584 FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
1587 local fvalue = self:formvalue(section) and self.enabled or self.disabled
1588 local cvalue = self:cfgvalue(section)
1590 fvalue, val_err = self:validate(fvalue, section)
1593 self:add_error(section, "invalid", val_err)
1597 if fvalue == self.default and (self.optional or self.rmempty) then
1598 self:remove(section)
1600 self:write(section, fvalue)
1602 if (fvalue ~= cvalue) then self.section.changed = true end
1604 self:remove(section)
1605 self.section.changed = true
1609 function Flag.cfgvalue(self, section)
1610 return AbstractValue.cfgvalue(self, section) or self.default
1612 function Flag.validate(self, value)
1617 ListValue - A one-line value predefined in a list
1618 widget: The widget that will be used (select, radio)
1620 ListValue = class(AbstractValue)
1622 function ListValue.__init__(self, ...)
1623 AbstractValue.__init__(self, ...)
1624 self.template = "cbi/lvalue"
1627 self.widget = "select"
1632 function ListValue.reset_values(self)
1638 function ListValue.value(self, key, val, ...)
1639 if luci.util.contains(self.keylist, key) then
1644 table.insert(self.keylist, tostring(key))
1645 table.insert(self.vallist, tostring(val))
1646 table.insert(self.deplist, {...})
1649 function ListValue.validate(self, val)
1650 if luci.util.contains(self.keylist, val) then
1660 MultiValue - Multiple delimited values
1661 widget: The widget that will be used (select, checkbox)
1662 delimiter: The delimiter that will separate the values (default: " ")
1664 MultiValue = class(AbstractValue)
1666 function MultiValue.__init__(self, ...)
1667 AbstractValue.__init__(self, ...)
1668 self.template = "cbi/mvalue"
1670 self.widget = "checkbox"
1671 self.delimiter = " "
1676 function MultiValue.render(self, ...)
1677 if self.widget == "select" and not self.size then
1678 self.size = #self.vallist
1681 AbstractValue.render(self, ...)
1684 function MultiValue.reset_values(self)
1690 function MultiValue.value(self, key, val)
1691 if luci.util.contains(self.keylist, key) then
1696 table.insert(self.keylist, tostring(key))
1697 table.insert(self.vallist, tostring(val))
1700 function MultiValue.valuelist(self, section)
1701 local val = self:cfgvalue(section)
1703 if not(type(val) == "string") then
1707 return luci.util.split(val, self.delimiter)
1710 function MultiValue.validate(self, val)
1711 val = (type(val) == "table") and val or {val}
1715 for i, value in ipairs(val) do
1716 if luci.util.contains(self.keylist, value) then
1717 result = result and (result .. self.delimiter .. value) or value
1725 StaticList = class(MultiValue)
1727 function StaticList.__init__(self, ...)
1728 MultiValue.__init__(self, ...)
1730 self.valuelist = self.cfgvalue
1732 if not self.override_scheme
1733 and self.map:get_scheme(self.section.sectiontype, self.option) then
1734 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1735 if self.value and vs.values and not self.override_values then
1736 for k, v in pairs(vs.values) do
1743 function StaticList.validate(self, value)
1744 value = (type(value) == "table") and value or {value}
1747 for i, v in ipairs(value) do
1748 if luci.util.contains(self.keylist, v) then
1749 table.insert(valid, v)
1756 DynamicList = class(AbstractValue)
1758 function DynamicList.__init__(self, ...)
1759 AbstractValue.__init__(self, ...)
1760 self.template = "cbi/dynlist"
1765 function DynamicList.reset_values(self)
1770 function DynamicList.value(self, key, val)
1772 table.insert(self.keylist, tostring(key))
1773 table.insert(self.vallist, tostring(val))
1776 function DynamicList.write(self, section, value)
1779 if type(value) == "table" then
1781 for _, x in ipairs(value) do
1782 if x and #x > 0 then
1790 if self.cast == "string" then
1791 value = table.concat(t, " ")
1796 return AbstractValue.write(self, section, value)
1799 function DynamicList.cfgvalue(self, section)
1800 local value = AbstractValue.cfgvalue(self, section)
1802 if type(value) == "string" then
1805 for x in value:gmatch("%S+") do
1816 function DynamicList.formvalue(self, section)
1817 local value = AbstractValue.formvalue(self, section)
1819 if type(value) == "string" then
1820 if self.cast == "string" then
1823 for x in value:gmatch("%S+") do
1837 TextValue - A multi-line value
1840 TextValue = class(AbstractValue)
1842 function TextValue.__init__(self, ...)
1843 AbstractValue.__init__(self, ...)
1844 self.template = "cbi/tvalue"
1850 Button = class(AbstractValue)
1852 function Button.__init__(self, ...)
1853 AbstractValue.__init__(self, ...)
1854 self.template = "cbi/button"
1855 self.inputstyle = nil
1857 self.unsafeupload = false
1861 FileUpload = class(AbstractValue)
1863 function FileUpload.__init__(self, ...)
1864 AbstractValue.__init__(self, ...)
1865 self.template = "cbi/upload"
1866 if not self.map.upload_fields then
1867 self.map.upload_fields = { self }
1869 self.map.upload_fields[#self.map.upload_fields+1] = self
1873 function FileUpload.formcreated(self, section)
1874 if self.unsafeupload then
1875 return AbstractValue.formcreated(self, section) or
1876 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1877 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") or
1878 self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
1880 return AbstractValue.formcreated(self, section) or
1881 self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
1885 function FileUpload.cfgvalue(self, section)
1886 local val = AbstractValue.cfgvalue(self, section)
1887 if val and fs.access(val) then
1893 -- If we have a new value, use it
1894 -- otherwise use old value
1895 -- deletion should be managed by a separate button object
1896 -- unless self.unsafeupload is set in which case if the user
1897 -- choose to remove the old file we do so.
1898 -- Also, allow to specify (via textbox) a file already on router
1899 function FileUpload.formvalue(self, section)
1900 local val = AbstractValue.formvalue(self, section)
1902 if self.unsafeupload then
1903 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1904 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1911 elseif val ~= "" then
1915 val = luci.http.formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
1919 if not self.unsafeupload then
1921 val = self.map:formvalue("cbi.rlf."..section.."."..self.option)
1927 function FileUpload.remove(self, section)
1928 if self.unsafeupload then
1929 local val = AbstractValue.formvalue(self, section)
1930 if val and fs.access(val) then fs.unlink(val) end
1931 return AbstractValue.remove(self, section)
1937 FileBrowser = class(AbstractValue)
1939 function FileBrowser.__init__(self, ...)
1940 AbstractValue.__init__(self, ...)
1941 self.template = "cbi/browser"