CBI: Fix subdependencies
[project/luci.git] / libs / cbi / luasrc / cbi.lua
1 --[[
2 LuCI - Configuration Bind Interface
3
4 Description:
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
7
8 FileId:
9 $Id$
10
11 License:
12 Copyright 2008 Steven Barth <steven@midlink.org>
13
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
17
18         http://www.apache.org/licenses/LICENSE-2.0
19
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.
25
26 ]]--
27 module("luci.cbi", package.seeall)
28
29 require("luci.template")
30 local util = require("luci.util")
31 require("luci.http")
32 require("luci.uvl")
33
34
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
40
41 FORM_NODATA  =  0
42 FORM_PROCEED =  0
43 FORM_VALID   =  1
44 FORM_DONE        =  1
45 FORM_INVALID = -1
46 FORM_CHANGED =  2
47 FORM_SKIP    =  4
48
49 AUTO = true
50
51 CREATE_PREFIX = "cbi.cts."
52 REMOVE_PREFIX = "cbi.rts."
53
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")
59         require("luci.util")
60
61         local upldir = "/lib/uci/upload/"
62         local cbidir = luci.util.libpath() .. "/model/cbi/"
63         local func, err
64
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")
71         else
72                 func, err = nil, "Model '" .. cbimap .. "' not found!"
73         end
74
75         assert(func, err)
76
77         luci.i18n.loadc("base")
78
79         local env = {
80                 translate=i18n.translate,
81                 translatef=i18n.translatef,
82                 arg={...}
83         }
84
85         setfenv(func, setmetatable(env, {__index =
86                 function(tbl, key)
87                         return rawget(tbl, key) or _M[key] or _G[key]
88                 end}))
89
90         local maps       = { func() }
91         local uploads    = { }
92         local has_upload = false
93
94         for i, map in ipairs(maps) do
95                 if not instanceof(map, Node) then
96                         error("CBI map returns no valid map object!")
97                         return nil
98                 else
99                         map:prepare()
100                         if map.upload_fields then
101                                 has_upload = true
102                                 for _, field in ipairs(map.upload_fields) do
103                                         uploads[
104                                                 field.config .. '.' ..
105                                                 field.section.sectiontype .. '.' ..
106                                                 field.option
107                                         ] = true
108                                 end
109                         end
110                 end
111         end
112
113         if has_upload then
114                 local uci = luci.model.uci.cursor()
115                 local prm = luci.http.context.request.message.params
116                 local fd, cbid
117
118                 luci.http.setfilehandler(
119                         function( field, chunk, eof )
120                                 if not field then return end
121                                 if field.name and not cbid then
122                                         local c, s, o = field.name:gmatch(
123                                                 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
124                                         )()
125
126                                         if c and s and o then
127                                                 local t = uci:get( c, s )
128                                                 if t and uploads[c.."."..t.."."..o] then
129                                                         local path = upldir .. field.name
130                                                         fd = io.open(path, "w")
131                                                         if fd then
132                                                                 cbid = field.name
133                                                                 prm[cbid] = path
134                                                         end
135                                                 end
136                                         end
137                                 end
138
139                                 if field.name == cbid and fd then
140                                         fd:write(chunk)
141                                 end
142
143                                 if eof and fd then
144                                         fd:close()
145                                         fd   = nil
146                                         cbid = nil
147                                 end
148                         end
149                 )
150         end
151
152         return maps
153 end
154
155 local function _uvl_validate_section(node, name)
156         local co = node.map:get()
157
158         luci.uvl.STRICT_UNKNOWN_OPTIONS = false
159         luci.uvl.STRICT_UNKNOWN_SECTIONS = false
160
161         local function tag_fields(e)
162                 if e.option and node.fields[e.option] then
163                         if node.fields[e.option].error then
164                                 node.fields[e.option].error[name] = e
165                         else
166                                 node.fields[e.option].error = { [name] = e }
167                         end
168                 elseif e.childs then
169                         for _, c in ipairs(e.childs) do tag_fields(c) end
170                 end
171         end
172
173         local function tag_section(e)
174                 local s = { }
175                 for _, c in ipairs(e.childs or { e }) do
176                         if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
177                                 table.insert( s, c.childs[1]:string() )
178                         else
179                                 table.insert( s, c:string() )
180                         end
181                 end
182                 if #s > 0 then
183                         if node.error then
184                                 node.error[name] = s
185                         else
186                                 node.error = { [name] = s }
187                         end
188                 end
189         end
190
191         local stat, err = node.map.validator:validate_section(node.config, name, co)
192         if err then
193                 node.map.save = false
194                 tag_fields(err)
195                 tag_section(err)
196         end
197
198 end
199
200 local function _uvl_strip_remote_dependencies(deps)
201         local clean = {}
202
203         for k, v in pairs(deps) do
204                 k = k:gsub("%$config%.%$section%.", "")
205                 if k:match("^[%w_]+$") and type(v) == "string" then
206                         clean[k] = v
207                 end
208         end
209
210         return clean
211 end
212
213
214 -- Node pseudo abstract class
215 Node = class()
216
217 function Node.__init__(self, title, description)
218         self.children = {}
219         self.title = title or ""
220         self.description = description or ""
221         self.template = "cbi/node"
222 end
223
224 -- hook helper
225 function Node._run_hooks(self, ...)
226         local f
227         local r = false
228         for _, f in ipairs(arg) do
229                 if type(self[f]) == "function" then
230                         self[f](self)
231                         r = true
232                 end
233         end
234         return r
235 end
236
237 -- Prepare nodes
238 function Node.prepare(self, ...)
239         for k, child in ipairs(self.children) do
240                 child:prepare(...)
241         end
242 end
243
244 -- Append child nodes
245 function Node.append(self, obj)
246         table.insert(self.children, obj)
247 end
248
249 -- Parse this node and its children
250 function Node.parse(self, ...)
251         for k, child in ipairs(self.children) do
252                 child:parse(...)
253         end
254 end
255
256 -- Render this node
257 function Node.render(self, scope)
258         scope = scope or {}
259         scope.self = self
260
261         luci.template.render(self.template, scope)
262 end
263
264 -- Render the children
265 function Node.render_children(self, ...)
266         for k, node in ipairs(self.children) do
267                 node:render(...)
268         end
269 end
270
271
272 --[[
273 A simple template element
274 ]]--
275 Template = class(Node)
276
277 function Template.__init__(self, template)
278         Node.__init__(self)
279         self.template = template
280 end
281
282 function Template.render(self)
283         luci.template.render(self.template, {self=self})
284 end
285
286 function Template.parse(self, readinput)
287         self.readinput = (readinput ~= false)
288         return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
289 end
290
291
292 --[[
293 Map - A map describing a configuration file
294 ]]--
295 Map = class(Node)
296
297 function Map.__init__(self, config, ...)
298         Node.__init__(self, ...)
299
300         self.config = config
301         self.parsechain = {self.config}
302         self.template = "cbi/map"
303         self.apply_on_parse = nil
304         self.readinput = true
305         self.proceed = false
306         self.flow = {}
307
308         self.uci = uci.cursor()
309         self.save = true
310
311         self.changed = false
312
313         if not self.uci:load(self.config) then
314                 error("Unable to read UCI data: " .. self.config)
315         end
316
317         self.validator = luci.uvl.UVL()
318         self.scheme = self.validator:get_scheme(self.config)
319 end
320
321 function Map.formvalue(self, key)
322         return self.readinput and luci.http.formvalue(key)
323 end
324
325 function Map.formvaluetable(self, key)
326         return self.readinput and luci.http.formvaluetable(key) or {}
327 end
328
329 function Map.get_scheme(self, sectiontype, option)
330         if not option then
331                 return self.scheme and self.scheme.sections[sectiontype]
332         else
333                 return self.scheme and self.scheme.variables[sectiontype]
334                  and self.scheme.variables[sectiontype][option]
335         end
336 end
337
338 function Map.submitstate(self)
339         return self:formvalue("cbi.submit")
340 end
341
342 -- Chain foreign config
343 function Map.chain(self, config)
344         table.insert(self.parsechain, config)
345 end
346
347 function Map.state_handler(self, state)
348         return state
349 end
350
351 -- Use optimized UCI writing
352 function Map.parse(self, readinput, ...)
353         self.readinput = (readinput ~= false)
354         self:_run_hooks("on_parse")
355
356         if self:formvalue("cbi.skip") then
357                 self.state = FORM_SKIP
358                 return self:state_handler(self.state)
359         end
360
361         Node.parse(self, ...)
362
363         if self.save then
364                 for i, config in ipairs(self.parsechain) do
365                         self.uci:save(config)
366                 end
367                 if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
368                         self:_run_hooks("on_before_commit")
369                         for i, config in ipairs(self.parsechain) do
370                                 self.uci:commit(config)
371
372                                 -- Refresh data because commit changes section names
373                                 self.uci:load(config)
374                         end
375                         self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
376                         if self.apply_on_parse then
377                                 self.uci:apply(self.parsechain)
378                                 self:_run_hooks("on_apply", "on_after_apply")
379                         else
380                                 self._apply = function()
381                                         local cmd = self.uci:apply(self.parsechain, true)
382                                         return io.popen(cmd)
383                                 end
384                         end
385
386                         -- Reparse sections
387                         Node.parse(self, true)
388
389                 end
390                 for i, config in ipairs(self.parsechain) do
391                         self.uci:unload(config)
392                 end
393                 if type(self.commit_handler) == "function" then
394                         self:commit_handler(self:submitstate())
395                 end
396         end
397
398         if self:submitstate() then
399                 if not self.save then
400                         self.state = FORM_INVALID
401                 elseif self.proceed then
402                         self.state = FORM_PROCEED
403                 else
404                         self.state = self.changed and FORM_CHANGED or FORM_VALID
405                 end
406         else
407                 self.state = FORM_NODATA
408         end
409
410         return self:state_handler(self.state)
411 end
412
413 function Map.render(self, ...)
414         self:_run_hooks("on_init")
415         Node.render(self, ...)
416         if self._apply then
417                 local fp = self._apply()
418                 fp:read("*a")
419                 fp:close()
420                 self:_run_hooks("on_apply")
421         end
422 end
423
424 -- Creates a child section
425 function Map.section(self, class, ...)
426         if instanceof(class, AbstractSection) then
427                 local obj  = class(self, ...)
428                 self:append(obj)
429                 return obj
430         else
431                 error("class must be a descendent of AbstractSection")
432         end
433 end
434
435 -- UCI add
436 function Map.add(self, sectiontype)
437         return self.uci:add(self.config, sectiontype)
438 end
439
440 -- UCI set
441 function Map.set(self, section, option, value)
442         if option then
443                 return self.uci:set(self.config, section, option, value)
444         else
445                 return self.uci:set(self.config, section, value)
446         end
447 end
448
449 -- UCI del
450 function Map.del(self, section, option)
451         if option then
452                 return self.uci:delete(self.config, section, option)
453         else
454                 return self.uci:delete(self.config, section)
455         end
456 end
457
458 -- UCI get
459 function Map.get(self, section, option)
460         if not section then
461                 return self.uci:get_all(self.config)
462         elseif option then
463                 return self.uci:get(self.config, section, option)
464         else
465                 return self.uci:get_all(self.config, section)
466         end
467 end
468
469 --[[
470 Compound - Container
471 ]]--
472 Compound = class(Node)
473
474 function Compound.__init__(self, ...)
475         Node.__init__(self)
476         self.template = "cbi/compound"
477         self.children = {...}
478 end
479
480 function Compound.populate_delegator(self, delegator)
481         for _, v in ipairs(self.children) do
482                 v.delegator = delegator
483         end
484 end
485
486 function Compound.parse(self, ...)
487         local cstate, state = 0
488
489         for k, child in ipairs(self.children) do
490                 cstate = child:parse(...)
491                 state = (not state or cstate < state) and cstate or state
492         end
493
494         return state
495 end
496
497
498 --[[
499 Delegator - Node controller
500 ]]--
501 Delegator = class(Node)
502 function Delegator.__init__(self, ...)
503         Node.__init__(self, ...)
504         self.nodes = {}
505         self.defaultpath = {}
506         self.pageaction = false
507         self.readinput = true
508         self.allow_reset = false
509         self.allow_cancel = false
510         self.allow_back = false
511         self.allow_finish = false
512         self.template = "cbi/delegator"
513 end
514
515 function Delegator.set(self, name, node)
516         if type(node) == "table" and getmetatable(node) == nil then
517                 node = Compound(unpack(node))
518         end
519         assert(type(node) == "function" or instanceof(node, Compound), "Invalid")
520         assert(not self.nodes[name], "Duplicate entry")
521
522         self.nodes[name] = node
523 end
524
525 function Delegator.add(self, name, node)
526         node = self:set(name, node)
527         self.defaultpath[#self.defaultpath+1] = name
528 end
529
530 function Delegator.insert_after(self, name, after)
531         local n = #self.chain
532         for k, v in ipairs(self.chain) do
533                 if v == state then
534                         n = k + 1
535                         break
536                 end
537         end
538         table.insert(self.chain, n, name)
539 end
540
541 function Delegator.set_route(self, ...)
542         local n, chain, route = 0, self.chain, {...}
543         for i = 1, #chain do
544                 if chain[i] == self.current then
545                         n = i
546                         break
547                 end
548         end
549         for i = 1, #route do
550                 n = n + 1
551                 chain[n] = route[i]
552         end
553         for i = n + 1, #chain do
554                 chain[i] = nil
555         end
556 end
557
558 function Delegator.get(self, name)
559         return self.nodes[name]
560 end
561
562 function Delegator.parse(self, ...)
563         if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
564                 if self:_run_hooks("on_cancel") then
565                         return FORM_DONE
566                 end
567         end
568         
569         if not Map.formvalue(self, "cbi.delg.current") then
570                 self:_run_hooks("on_init")
571         end
572
573         local newcurrent
574         self.chain = self.chain or self:get_chain()
575         self.current = self.current or self:get_active()
576         self.active = self.active or self:get(self.current)
577         assert(self.active, "Invalid state")
578         
579         local stat = FORM_DONE
580         if type(self.active) ~= "function" then
581                 self.active:populate_delegator(self)
582                 stat = self.active:parse() 
583         else
584                 self:active()
585         end
586
587         if stat > FORM_PROCEED then
588                 if Map.formvalue(self, "cbi.delg.back") then
589                         newcurrent = self:get_prev(self.current)
590                 else
591                         newcurrent = self:get_next(self.current)
592                 end
593         elseif stat < FORM_PROCEED then
594                 return stat
595         end
596         
597
598         if not Map.formvalue(self, "cbi.submit") then
599                 return FORM_NODATA
600         elseif stat > FORM_PROCEED 
601         and (not newcurrent or not self:get(newcurrent)) then
602                 self:_run_hooks("on_done")
603                 return FORM_DONE
604         else
605                 self.current = newcurrent or self.current
606                 self.active = self:get(self.current)
607                 if type(self.active) ~= "function" then
608                         self.active:parse(false)
609                         return FROM_PROCEED
610                 else
611                         return self:parse(...)
612                 end
613         end
614 end
615
616 function Delegator.get_next(self, state)
617         for k, v in ipairs(self.chain) do
618                 if v == state then
619                         return self.chain[k+1]
620                 end
621         end
622 end
623
624 function Delegator.get_prev(self, state)
625         for k, v in ipairs(self.chain) do
626                 if v == state then
627                         return self.chain[k-1]
628                 end
629         end
630 end
631
632 function Delegator.get_chain(self)
633         local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
634         return type(x) == "table" and x or {x}
635 end
636
637 function Delegator.get_active(self)
638         return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
639 end
640
641 --[[
642 Page - A simple node
643 ]]--
644
645 Page = class(Node)
646 Page.__init__ = Node.__init__
647 Page.parse    = function() end
648
649
650 --[[
651 SimpleForm - A Simple non-UCI form
652 ]]--
653 SimpleForm = class(Node)
654
655 function SimpleForm.__init__(self, config, title, description, data)
656         Node.__init__(self, title, description)
657         self.config = config
658         self.data = data or {}
659         self.template = "cbi/simpleform"
660         self.dorender = true
661         self.pageaction = false
662         self.readinput = true
663 end
664
665 SimpleForm.formvalue = Map.formvalue
666 SimpleForm.formvaluetable = Map.formvaluetable
667
668 function SimpleForm.parse(self, readinput, ...)
669         self.readinput = (readinput ~= false)
670
671         if self:formvalue("cbi.skip") then
672                 return FORM_SKIP
673         end
674
675         if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
676                 return FORM_DONE
677         end
678
679         if self:submitstate() then
680                 Node.parse(self, 1, ...)
681         end
682
683         local valid = true
684         for k, j in ipairs(self.children) do
685                 for i, v in ipairs(j.children) do
686                         valid = valid
687                          and (not v.tag_missing or not v.tag_missing[1])
688                          and (not v.tag_invalid or not v.tag_invalid[1])
689                          and (not v.error)
690                 end
691         end
692
693         local state =
694                 not self:submitstate() and FORM_NODATA
695                 or valid and FORM_VALID
696                 or FORM_INVALID
697
698         self.dorender = not self.handle
699         if self.handle then
700                 local nrender, nstate = self:handle(state, self.data)
701                 self.dorender = self.dorender or (nrender ~= false)
702                 state = nstate or state
703         end
704         return state
705 end
706
707 function SimpleForm.render(self, ...)
708         if self.dorender then
709                 Node.render(self, ...)
710         end
711 end
712
713 function SimpleForm.submitstate(self)
714         return self:formvalue("cbi.submit")
715 end
716
717 function SimpleForm.section(self, class, ...)
718         if instanceof(class, AbstractSection) then
719                 local obj  = class(self, ...)
720                 self:append(obj)
721                 return obj
722         else
723                 error("class must be a descendent of AbstractSection")
724         end
725 end
726
727 -- Creates a child field
728 function SimpleForm.field(self, class, ...)
729         local section
730         for k, v in ipairs(self.children) do
731                 if instanceof(v, SimpleSection) then
732                         section = v
733                         break
734                 end
735         end
736         if not section then
737                 section = self:section(SimpleSection)
738         end
739
740         if instanceof(class, AbstractValue) then
741                 local obj  = class(self, section, ...)
742                 obj.track_missing = true
743                 section:append(obj)
744                 return obj
745         else
746                 error("class must be a descendent of AbstractValue")
747         end
748 end
749
750 function SimpleForm.set(self, section, option, value)
751         self.data[option] = value
752 end
753
754
755 function SimpleForm.del(self, section, option)
756         self.data[option] = nil
757 end
758
759
760 function SimpleForm.get(self, section, option)
761         return self.data[option]
762 end
763
764
765 function SimpleForm.get_scheme()
766         return nil
767 end
768
769
770 Form = class(SimpleForm)
771
772 function Form.__init__(self, ...)
773         SimpleForm.__init__(self, ...)
774         self.embedded = true
775 end
776
777
778 --[[
779 AbstractSection
780 ]]--
781 AbstractSection = class(Node)
782
783 function AbstractSection.__init__(self, map, sectiontype, ...)
784         Node.__init__(self, ...)
785         self.sectiontype = sectiontype
786         self.map = map
787         self.config = map.config
788         self.optionals = {}
789         self.defaults = {}
790         self.fields = {}
791         self.tag_error = {}
792         self.tag_invalid = {}
793         self.tag_deperror = {}
794         self.changed = false
795
796         self.optional = true
797         self.addremove = false
798         self.dynamic = false
799 end
800
801 -- Define a tab for the section
802 function AbstractSection.tab(self, tab, title, desc)
803         self.tabs      = self.tabs      or { }
804         self.tab_names = self.tab_names or { }
805
806         self.tab_names[#self.tab_names+1] = tab
807         self.tabs[tab] = {
808                 title       = title,
809                 description = desc,
810                 childs      = { }
811         }
812 end
813
814 -- Appends a new option
815 function AbstractSection.option(self, class, option, ...)
816         -- Autodetect from UVL
817         if class == true and self.map:get_scheme(self.sectiontype, option) then
818                 local vs = self.map:get_scheme(self.sectiontype, option)
819                 if vs.type == "boolean" then
820                         class = Flag
821                 elseif vs.type == "list" then
822                         class = DynamicList
823                 elseif vs.type == "enum" or vs.type == "reference" then
824                         class = ListValue
825                 else
826                         class = Value
827                 end
828         end
829
830         if instanceof(class, AbstractValue) then
831                 local obj  = class(self.map, self, option, ...)
832                 self:append(obj)
833                 self.fields[option] = obj
834                 return obj
835         elseif class == true then
836                 error("No valid class was given and autodetection failed.")
837         else
838                 error("class must be a descendant of AbstractValue")
839         end
840 end
841
842 -- Appends a new tabbed option
843 function AbstractSection.taboption(self, tab, ...)
844
845         assert(tab and self.tabs and self.tabs[tab],
846                 "Cannot assign option to not existing tab %q" % tostring(tab))
847
848         local l = self.tabs[tab].childs
849         local o = AbstractSection.option(self, ...)
850
851         if o then l[#l+1] = o end
852
853         return o
854 end
855
856 -- Render a single tab
857 function AbstractSection.render_tab(self, tab, ...)
858
859         assert(tab and self.tabs and self.tabs[tab],
860                 "Cannot render not existing tab %q" % tostring(tab))
861
862         for _, node in ipairs(self.tabs[tab].childs) do
863                 node:render(...)
864         end
865 end
866
867 -- Parse optional options
868 function AbstractSection.parse_optionals(self, section)
869         if not self.optional then
870                 return
871         end
872
873         self.optionals[section] = {}
874
875         local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
876         for k,v in ipairs(self.children) do
877                 if v.optional and not v:cfgvalue(section) then
878                         if field == v.option then
879                                 field = nil
880                                 self.map.proceed = true
881                         else
882                                 table.insert(self.optionals[section], v)
883                         end
884                 end
885         end
886
887         if field and #field > 0 and self.dynamic then
888                 self:add_dynamic(field)
889         end
890 end
891
892 -- Add a dynamic option
893 function AbstractSection.add_dynamic(self, field, optional)
894         local o = self:option(Value, field, field)
895         o.optional = optional
896 end
897
898 -- Parse all dynamic options
899 function AbstractSection.parse_dynamic(self, section)
900         if not self.dynamic then
901                 return
902         end
903
904         local arr  = luci.util.clone(self:cfgvalue(section))
905         local form = self.map:formvaluetable("cbid."..self.config.."."..section)
906         for k, v in pairs(form) do
907                 arr[k] = v
908         end
909
910         for key,val in pairs(arr) do
911                 local create = true
912
913                 for i,c in ipairs(self.children) do
914                         if c.option == key then
915                                 create = false
916                         end
917                 end
918
919                 if create and key:sub(1, 1) ~= "." then
920                         self.map.proceed = true
921                         self:add_dynamic(key, true)
922                 end
923         end
924 end
925
926 -- Returns the section's UCI table
927 function AbstractSection.cfgvalue(self, section)
928         return self.map:get(section)
929 end
930
931 -- Push events
932 function AbstractSection.push_events(self)
933         --luci.util.append(self.map.events, self.events)
934         self.map.changed = true
935 end
936
937 -- Removes the section
938 function AbstractSection.remove(self, section)
939         self.map.proceed = true
940         return self.map:del(section)
941 end
942
943 -- Creates the section
944 function AbstractSection.create(self, section)
945         local stat
946
947         if section then
948                 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
949         else
950                 section = self.map:add(self.sectiontype)
951                 stat = section
952         end
953
954         if stat then
955                 for k,v in pairs(self.children) do
956                         if v.default then
957                                 self.map:set(section, v.option, v.default)
958                         end
959                 end
960
961                 for k,v in pairs(self.defaults) do
962                         self.map:set(section, k, v)
963                 end
964         end
965
966         self.map.proceed = true
967
968         return stat
969 end
970
971
972 SimpleSection = class(AbstractSection)
973
974 function SimpleSection.__init__(self, form, ...)
975         AbstractSection.__init__(self, form, nil, ...)
976         self.template = "cbi/nullsection"
977 end
978
979
980 Table = class(AbstractSection)
981
982 function Table.__init__(self, form, data, ...)
983         local datasource = {}
984         local tself = self
985         datasource.config = "table"
986         self.data = data or {}
987
988         datasource.formvalue = Map.formvalue
989         datasource.formvaluetable = Map.formvaluetable
990         datasource.readinput = true
991
992         function datasource.get(self, section, option)
993                 return tself.data[section] and tself.data[section][option]
994         end
995
996         function datasource.submitstate(self)
997                 return Map.formvalue(self, "cbi.submit")
998         end
999
1000         function datasource.del(...)
1001                 return true
1002         end
1003
1004         function datasource.get_scheme()
1005                 return nil
1006         end
1007
1008         AbstractSection.__init__(self, datasource, "table", ...)
1009         self.template = "cbi/tblsection"
1010         self.rowcolors = true
1011         self.anonymous = true
1012 end
1013
1014 function Table.parse(self, readinput)
1015         self.map.readinput = (readinput ~= false)
1016         for i, k in ipairs(self:cfgsections()) do
1017                 if self.map:submitstate() then
1018                         Node.parse(self, k)
1019                 end
1020         end
1021 end
1022
1023 function Table.cfgsections(self)
1024         local sections = {}
1025
1026         for i, v in luci.util.kspairs(self.data) do
1027                 table.insert(sections, i)
1028         end
1029
1030         return sections
1031 end
1032
1033 function Table.update(self, data)
1034         self.data = data
1035 end
1036
1037
1038
1039 --[[
1040 NamedSection - A fixed configuration section defined by its name
1041 ]]--
1042 NamedSection = class(AbstractSection)
1043
1044 function NamedSection.__init__(self, map, section, stype, ...)
1045         AbstractSection.__init__(self, map, stype, ...)
1046
1047         -- Defaults
1048         self.addremove = false
1049
1050         -- Use defaults from UVL
1051         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1052                 local vs = self.map:get_scheme(self.sectiontype)
1053                 self.addremove = not vs.unique and not vs.required
1054                 self.dynamic   = vs.dynamic
1055                 self.title       = self.title or vs.title
1056                 self.description = self.description or vs.descr
1057         end
1058
1059         self.template = "cbi/nsection"
1060         self.section = section
1061 end
1062
1063 function NamedSection.parse(self, novld)
1064         local s = self.section
1065         local active = self:cfgvalue(s)
1066
1067         if self.addremove then
1068                 local path = self.config.."."..s
1069                 if active then -- Remove the section
1070                         if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1071                                 self:push_events()
1072                                 return
1073                         end
1074                 else           -- Create and apply default values
1075                         if self.map:formvalue("cbi.cns."..path) then
1076                                 self:create(s)
1077                                 return
1078                         end
1079                 end
1080         end
1081
1082         if active then
1083                 AbstractSection.parse_dynamic(self, s)
1084                 if self.map:submitstate() then
1085                         Node.parse(self, s)
1086
1087                         if not novld and not self.override_scheme and self.map.scheme then
1088                                 _uvl_validate_section(self, s)
1089                         end
1090                 end
1091                 AbstractSection.parse_optionals(self, s)
1092
1093                 if self.changed then
1094                         self:push_events()
1095                 end
1096         end
1097 end
1098
1099
1100 --[[
1101 TypedSection - A (set of) configuration section(s) defined by the type
1102         addremove:      Defines whether the user can add/remove sections of this type
1103         anonymous:  Allow creating anonymous sections
1104         validate:       a validation function returning nil if the section is invalid
1105 ]]--
1106 TypedSection = class(AbstractSection)
1107
1108 function TypedSection.__init__(self, map, type, ...)
1109         AbstractSection.__init__(self, map, type, ...)
1110
1111         self.template  = "cbi/tsection"
1112         self.deps = {}
1113         self.anonymous = false
1114
1115         -- Use defaults from UVL
1116         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1117                 local vs = self.map:get_scheme(self.sectiontype)
1118                 self.addremove = not vs.unique and not vs.required
1119                 self.dynamic   = vs.dynamic
1120                 self.anonymous = not vs.named
1121                 self.title       = self.title or vs.title
1122                 self.description = self.description or vs.descr
1123         end
1124 end
1125
1126 -- Return all matching UCI sections for this TypedSection
1127 function TypedSection.cfgsections(self)
1128         local sections = {}
1129         self.map.uci:foreach(self.map.config, self.sectiontype,
1130                 function (section)
1131                         if self:checkscope(section[".name"]) then
1132                                 table.insert(sections, section[".name"])
1133                         end
1134                 end)
1135
1136         return sections
1137 end
1138
1139 -- Limits scope to sections that have certain option => value pairs
1140 function TypedSection.depends(self, option, value)
1141         table.insert(self.deps, {option=option, value=value})
1142 end
1143
1144 function TypedSection.parse(self, novld)
1145         if self.addremove then
1146                 -- Remove
1147                 local crval = REMOVE_PREFIX .. self.config
1148                 local name = self.map:formvaluetable(crval)
1149                 for k,v in pairs(name) do
1150                         if k:sub(-2) == ".x" then
1151                                 k = k:sub(1, #k - 2)
1152                         end
1153                         if self:cfgvalue(k) and self:checkscope(k) then
1154                                 self:remove(k)
1155                         end
1156                 end
1157         end
1158
1159         local co
1160         for i, k in ipairs(self:cfgsections()) do
1161                 AbstractSection.parse_dynamic(self, k)
1162                 if self.map:submitstate() then
1163                         Node.parse(self, k, novld)
1164
1165                         if not novld and not self.override_scheme and self.map.scheme then
1166                                 _uvl_validate_section(self, k)
1167                         end
1168                 end
1169                 AbstractSection.parse_optionals(self, k)
1170         end
1171
1172         if self.addremove then
1173                 -- Create
1174                 local created
1175                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1176                 local name  = self.map:formvalue(crval)
1177                 if self.anonymous then
1178                         if name then
1179                                 created = self:create()
1180                         end
1181                 else
1182                         if name then
1183                                 -- Ignore if it already exists
1184                                 if self:cfgvalue(name) then
1185                                         name = nil;
1186                                 end
1187
1188                                 name = self:checkscope(name)
1189
1190                                 if not name then
1191                                         self.err_invalid = true
1192                                 end
1193
1194                                 if name and #name > 0 then
1195                                         created = self:create(name) and name
1196                                         if not created then
1197                                                 self.invalid_cts = true
1198                                         end
1199                                 end
1200                         end
1201                 end
1202
1203                 if created then
1204                         AbstractSection.parse_optionals(self, created)
1205                 end
1206         end
1207
1208         if created or self.changed then
1209                 self:push_events()
1210         end
1211 end
1212
1213 -- Verifies scope of sections
1214 function TypedSection.checkscope(self, section)
1215         -- Check if we are not excluded
1216         if self.filter and not self:filter(section) then
1217                 return nil
1218         end
1219
1220         -- Check if at least one dependency is met
1221         if #self.deps > 0 and self:cfgvalue(section) then
1222                 local stat = false
1223
1224                 for k, v in ipairs(self.deps) do
1225                         if self:cfgvalue(section)[v.option] == v.value then
1226                                 stat = true
1227                         end
1228                 end
1229
1230                 if not stat then
1231                         return nil
1232                 end
1233         end
1234
1235         return self:validate(section)
1236 end
1237
1238
1239 -- Dummy validate function
1240 function TypedSection.validate(self, section)
1241         return section
1242 end
1243
1244
1245 --[[
1246 AbstractValue - An abstract Value Type
1247         null:           Value can be empty
1248         valid:          A function returning the value if it is valid otherwise nil
1249         depends:        A table of option => value pairs of which one must be true
1250         default:        The default value
1251         size:           The size of the input fields
1252         rmempty:        Unset value if empty
1253         optional:       This value is optional (see AbstractSection.optionals)
1254 ]]--
1255 AbstractValue = class(Node)
1256
1257 function AbstractValue.__init__(self, map, section, option, ...)
1258         Node.__init__(self, ...)
1259         self.section = section
1260         self.option  = option
1261         self.map     = map
1262         self.config  = map.config
1263         self.tag_invalid = {}
1264         self.tag_missing = {}
1265         self.tag_reqerror = {}
1266         self.tag_error = {}
1267         self.deps = {}
1268         self.subdeps = {}
1269         --self.cast = "string"
1270
1271         self.track_missing = false
1272         self.rmempty   = true
1273         self.default   = nil
1274         self.size      = nil
1275         self.optional  = false
1276 end
1277
1278 function AbstractValue.prepare(self)
1279         -- Use defaults from UVL
1280         if not self.override_scheme
1281          and self.map:get_scheme(self.section.sectiontype, self.option) then
1282                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1283                 if self.cast == nil then
1284                         self.cast = (vs.type == "list") and "list" or "string"
1285                 end
1286                 self.title       = self.title or vs.title
1287                 self.description = self.description or vs.descr
1288                 if self.default == nil then
1289                         self.default = vs.default
1290                 end
1291
1292                 if vs.depends and not self.override_dependencies then
1293                         for i, deps in ipairs(vs.depends) do
1294                                 deps = _uvl_strip_remote_dependencies(deps)
1295                                 if next(deps) then
1296                                         self:depends(deps)
1297                                 end
1298                         end
1299                 end
1300         end
1301
1302         self.cast = self.cast or "string"
1303 end
1304
1305 -- Add a dependencie to another section field
1306 function AbstractValue.depends(self, field, value)
1307         local deps
1308         if type(field) == "string" then
1309                 deps = {}
1310                 deps[field] = value
1311         else
1312                 deps = field
1313         end
1314
1315         table.insert(self.deps, {deps=deps, add=""})
1316 end
1317
1318 -- Generates the unique CBID
1319 function AbstractValue.cbid(self, section)
1320         return "cbid."..self.map.config.."."..section.."."..self.option
1321 end
1322
1323 -- Return whether this object should be created
1324 function AbstractValue.formcreated(self, section)
1325         local key = "cbi.opt."..self.config.."."..section
1326         return (self.map:formvalue(key) == self.option)
1327 end
1328
1329 -- Returns the formvalue for this object
1330 function AbstractValue.formvalue(self, section)
1331         return self.map:formvalue(self:cbid(section))
1332 end
1333
1334 function AbstractValue.additional(self, value)
1335         self.optional = value
1336 end
1337
1338 function AbstractValue.mandatory(self, value)
1339         self.rmempty = not value
1340 end
1341
1342 function AbstractValue.parse(self, section, novld)
1343         local fvalue = self:formvalue(section)
1344         local cvalue = self:cfgvalue(section)
1345
1346         -- If favlue and cvalue are both tables and have the same content
1347         -- make them identical
1348         if type(fvalue) == "table" and type(cvalue) == "table" then
1349                 local equal = #fvalue == #cvalue
1350                 if equal then
1351                         for i=1, #fvalue do
1352                                 if cvalue[i] ~= fvalue[i] then
1353                                         equal = false
1354                                 end
1355                         end
1356                 end
1357                 if equal then
1358                         fvalue = cvalue
1359                 end
1360         end
1361
1362         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1363                 fvalue = self:transform(self:validate(fvalue, section))
1364                 if not fvalue and not novld then
1365                         if self.error then
1366                                 self.error[section] = "invalid"
1367                         else
1368                                 self.error = { [section] = "invalid" }
1369                         end
1370                         if self.section.error then
1371                                 table.insert(self.section.error[section], "invalid")
1372                         else
1373                                 self.section.error = {[section] = {"invalid"}}
1374                         end 
1375                         self.map.save = false
1376                 end
1377                 if fvalue and not (fvalue == cvalue) then
1378                         if self:write(section, fvalue) then
1379                                 -- Push events
1380                                 self.section.changed = true
1381                                 --luci.util.append(self.map.events, self.events)
1382                         end
1383                 end
1384         else                                                    -- Unset the UCI or error
1385                 if self.rmempty or self.optional then
1386                         if self:remove(section) then
1387                                 -- Push events
1388                                 self.section.changed = true
1389                                 --luci.util.append(self.map.events, self.events)
1390                         end
1391                 elseif cvalue ~= fvalue and not novld then
1392                         self:write(section, fvalue or "")
1393                         if self.error then
1394                                 self.error[section] = "missing"
1395                         else
1396                                 self.error = { [section] = "missing" }
1397                         end
1398                         self.map.save = false
1399                 end
1400         end
1401 end
1402
1403 -- Render if this value exists or if it is mandatory
1404 function AbstractValue.render(self, s, scope)
1405         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1406                 scope = scope or {}
1407                 scope.section   = s
1408                 scope.cbid      = self:cbid(s)
1409                 scope.striptags = luci.util.striptags
1410                 scope.pcdata    = luci.util.pcdata
1411
1412                 scope.ifattr = function(cond,key,val)
1413                         if cond then
1414                                 return string.format(
1415                                         ' %s="%s"', tostring(key),
1416                                         luci.util.pcdata(tostring( val
1417                                          or scope[key]
1418                                          or (type(self[key]) ~= "function" and self[key])
1419                                          or "" ))
1420                                 )
1421                         else
1422                                 return ''
1423                         end
1424                 end
1425
1426                 scope.attr = function(...)
1427                         return scope.ifattr( true, ... )
1428                 end
1429
1430                 Node.render(self, scope)
1431         end
1432 end
1433
1434 -- Return the UCI value of this object
1435 function AbstractValue.cfgvalue(self, section)
1436         local value = self.map:get(section, self.option)
1437         if not value then
1438                 return nil
1439         elseif not self.cast or self.cast == type(value) then
1440                 return value
1441         elseif self.cast == "string" then
1442                 if type(value) == "table" then
1443                         return value[1]
1444                 end
1445         elseif self.cast == "table" then
1446                 return luci.util.split(value, "%s+", nil, true)
1447         end
1448 end
1449
1450 -- Validate the form value
1451 function AbstractValue.validate(self, value)
1452         return value
1453 end
1454
1455 AbstractValue.transform = AbstractValue.validate
1456
1457
1458 -- Write to UCI
1459 function AbstractValue.write(self, section, value)
1460         return self.map:set(section, self.option, value)
1461 end
1462
1463 -- Remove from UCI
1464 function AbstractValue.remove(self, section)
1465         return self.map:del(section, self.option)
1466 end
1467
1468
1469
1470
1471 --[[
1472 Value - A one-line value
1473         maxlength:      The maximum length
1474 ]]--
1475 Value = class(AbstractValue)
1476
1477 function Value.__init__(self, ...)
1478         AbstractValue.__init__(self, ...)
1479         self.template  = "cbi/value"
1480         self.keylist = {}
1481         self.vallist = {}
1482 end
1483
1484 function Value.value(self, key, val)
1485         val = val or key
1486         table.insert(self.keylist, tostring(key))
1487         table.insert(self.vallist, tostring(val))
1488 end
1489
1490
1491 -- DummyValue - This does nothing except being there
1492 DummyValue = class(AbstractValue)
1493
1494 function DummyValue.__init__(self, ...)
1495         AbstractValue.__init__(self, ...)
1496         self.template = "cbi/dvalue"
1497         self.value = nil
1498 end
1499
1500 function DummyValue.cfgvalue(self, section)
1501         local value
1502         if self.value then
1503                 if type(self.value) == "function" then
1504                         value = self:value(section)
1505                 else
1506                         value = self.value
1507                 end
1508         else
1509                 value = AbstractValue.cfgvalue(self, section)
1510         end
1511         return value
1512 end
1513
1514 function DummyValue.parse(self)
1515
1516 end
1517
1518
1519 --[[
1520 Flag - A flag being enabled or disabled
1521 ]]--
1522 Flag = class(AbstractValue)
1523
1524 function Flag.__init__(self, ...)
1525         AbstractValue.__init__(self, ...)
1526         self.template  = "cbi/fvalue"
1527
1528         self.enabled = "1"
1529         self.disabled = "0"
1530 end
1531
1532 -- A flag can only have two states: set or unset
1533 function Flag.parse(self, section)
1534         local fvalue = self:formvalue(section)
1535
1536         if fvalue then
1537                 fvalue = self.enabled
1538         else
1539                 fvalue = self.disabled
1540         end
1541
1542         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1543                 if not(fvalue == self:cfgvalue(section)) then
1544                         self:write(section, fvalue)
1545                 end
1546         else
1547                 self:remove(section)
1548         end
1549 end
1550
1551
1552
1553 --[[
1554 ListValue - A one-line value predefined in a list
1555         widget: The widget that will be used (select, radio)
1556 ]]--
1557 ListValue = class(AbstractValue)
1558
1559 function ListValue.__init__(self, ...)
1560         AbstractValue.__init__(self, ...)
1561         self.template  = "cbi/lvalue"
1562
1563         self.keylist = {}
1564         self.vallist = {}
1565         self.size   = 1
1566         self.widget = "select"
1567 end
1568
1569 function ListValue.prepare(self, ...)
1570         AbstractValue.prepare(self, ...)
1571         if not self.override_scheme
1572          and self.map:get_scheme(self.section.sectiontype, self.option) then
1573                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1574                 if self.value and vs.valuelist and not self.override_values then
1575                         for k, v in ipairs(vs.valuelist) do
1576                                 local deps = {}
1577                                 if not self.override_dependencies
1578                                  and vs.enum_depends and vs.enum_depends[v.value] then
1579                                         for i, dep in ipairs(vs.enum_depends[v.value]) do
1580                                                 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1581                                         end
1582                                 end
1583                                 self:value(v.value, v.title or v.value, unpack(deps))
1584                         end
1585                 end
1586         end
1587 end
1588
1589 function ListValue.value(self, key, val, ...)
1590         if luci.util.contains(self.keylist, key) then
1591                 return
1592         end
1593
1594         val = val or key
1595         table.insert(self.keylist, tostring(key))
1596         table.insert(self.vallist, tostring(val))
1597
1598         for i, deps in ipairs({...}) do
1599                 self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
1600         end
1601 end
1602
1603 function ListValue.validate(self, val)
1604         if luci.util.contains(self.keylist, val) then
1605                 return val
1606         else
1607                 return nil
1608         end
1609 end
1610
1611
1612
1613 --[[
1614 MultiValue - Multiple delimited values
1615         widget: The widget that will be used (select, checkbox)
1616         delimiter: The delimiter that will separate the values (default: " ")
1617 ]]--
1618 MultiValue = class(AbstractValue)
1619
1620 function MultiValue.__init__(self, ...)
1621         AbstractValue.__init__(self, ...)
1622         self.template = "cbi/mvalue"
1623
1624         self.keylist = {}
1625         self.vallist = {}
1626
1627         self.widget = "checkbox"
1628         self.delimiter = " "
1629 end
1630
1631 function MultiValue.render(self, ...)
1632         if self.widget == "select" and not self.size then
1633                 self.size = #self.vallist
1634         end
1635
1636         AbstractValue.render(self, ...)
1637 end
1638
1639 function MultiValue.value(self, key, val)
1640         if luci.util.contains(self.keylist, key) then
1641                 return
1642         end
1643
1644         val = val or key
1645         table.insert(self.keylist, tostring(key))
1646         table.insert(self.vallist, tostring(val))
1647 end
1648
1649 function MultiValue.valuelist(self, section)
1650         local val = self:cfgvalue(section)
1651
1652         if not(type(val) == "string") then
1653                 return {}
1654         end
1655
1656         return luci.util.split(val, self.delimiter)
1657 end
1658
1659 function MultiValue.validate(self, val)
1660         val = (type(val) == "table") and val or {val}
1661
1662         local result
1663
1664         for i, value in ipairs(val) do
1665                 if luci.util.contains(self.keylist, value) then
1666                         result = result and (result .. self.delimiter .. value) or value
1667                 end
1668         end
1669
1670         return result
1671 end
1672
1673
1674 StaticList = class(MultiValue)
1675
1676 function StaticList.__init__(self, ...)
1677         MultiValue.__init__(self, ...)
1678         self.cast = "table"
1679         self.valuelist = self.cfgvalue
1680
1681         if not self.override_scheme
1682          and self.map:get_scheme(self.section.sectiontype, self.option) then
1683                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1684                 if self.value and vs.values and not self.override_values then
1685                         for k, v in pairs(vs.values) do
1686                                 self:value(k, v)
1687                         end
1688                 end
1689         end
1690 end
1691
1692 function StaticList.validate(self, value)
1693         value = (type(value) == "table") and value or {value}
1694
1695         local valid = {}
1696         for i, v in ipairs(value) do
1697                 if luci.util.contains(self.keylist, v) then
1698                         table.insert(valid, v)
1699                 end
1700         end
1701         return valid
1702 end
1703
1704
1705 DynamicList = class(AbstractValue)
1706
1707 function DynamicList.__init__(self, ...)
1708         AbstractValue.__init__(self, ...)
1709         self.template  = "cbi/dynlist"
1710         self.cast = "table"
1711         self.keylist = {}
1712         self.vallist = {}
1713 end
1714
1715 function DynamicList.value(self, key, val)
1716         val = val or key
1717         table.insert(self.keylist, tostring(key))
1718         table.insert(self.vallist, tostring(val))
1719 end
1720
1721 function DynamicList.write(self, ...)
1722         self.map.proceed = true
1723         return AbstractValue.write(self, ...)
1724 end
1725
1726 function DynamicList.formvalue(self, section)
1727         local value = AbstractValue.formvalue(self, section)
1728         value = (type(value) == "table") and value or {value}
1729
1730         local valid = {}
1731         for i, v in ipairs(value) do
1732                 if v and #v > 0
1733                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1734                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1735                         table.insert(valid, v)
1736                 end
1737         end
1738
1739         return valid
1740 end
1741
1742
1743 --[[
1744 TextValue - A multi-line value
1745         rows:   Rows
1746 ]]--
1747 TextValue = class(AbstractValue)
1748
1749 function TextValue.__init__(self, ...)
1750         AbstractValue.__init__(self, ...)
1751         self.template  = "cbi/tvalue"
1752 end
1753
1754 --[[
1755 Button
1756 ]]--
1757 Button = class(AbstractValue)
1758
1759 function Button.__init__(self, ...)
1760         AbstractValue.__init__(self, ...)
1761         self.template  = "cbi/button"
1762         self.inputstyle = nil
1763         self.rmempty = true
1764 end
1765
1766
1767 FileUpload = class(AbstractValue)
1768
1769 function FileUpload.__init__(self, ...)
1770         AbstractValue.__init__(self, ...)
1771         self.template = "cbi/upload"
1772         if not self.map.upload_fields then
1773                 self.map.upload_fields = { self }
1774         else
1775                 self.map.upload_fields[#self.map.upload_fields+1] = self
1776         end
1777 end
1778
1779 function FileUpload.formcreated(self, section)
1780         return AbstractValue.formcreated(self, section) or
1781                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1782                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1783 end
1784
1785 function FileUpload.cfgvalue(self, section)
1786         local val = AbstractValue.cfgvalue(self, section)
1787         if val and fs.access(val) then
1788                 return val
1789         end
1790         return nil
1791 end
1792
1793 function FileUpload.formvalue(self, section)
1794         local val = AbstractValue.formvalue(self, section)
1795         if val then
1796                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1797                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1798                 then
1799                         return val
1800                 end
1801                 fs.unlink(val)
1802                 self.value = nil
1803         end
1804         return nil
1805 end
1806
1807 function FileUpload.remove(self, section)
1808         local val = AbstractValue.formvalue(self, section)
1809         if val and fs.access(val) then fs.unlink(val) end
1810         return AbstractValue.remove(self, section)
1811 end
1812
1813
1814 FileBrowser = class(AbstractValue)
1815
1816 function FileBrowser.__init__(self, ...)
1817         AbstractValue.__init__(self, ...)
1818         self.template = "cbi/browser"
1819 end