libs/cbi: implement on_parse hook for maps
[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.cast = "string"
1269
1270         self.track_missing = false
1271         self.rmempty   = true
1272         self.default   = nil
1273         self.size      = nil
1274         self.optional  = false
1275 end
1276
1277 function AbstractValue.prepare(self)
1278         -- Use defaults from UVL
1279         if not self.override_scheme
1280          and self.map:get_scheme(self.section.sectiontype, self.option) then
1281                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1282                 if self.cast == nil then
1283                         self.cast = (vs.type == "list") and "list" or "string"
1284                 end
1285                 self.title       = self.title or vs.title
1286                 self.description = self.description or vs.descr
1287                 if self.default == nil then
1288                         self.default = vs.default
1289                 end
1290
1291                 if vs.depends and not self.override_dependencies then
1292                         for i, deps in ipairs(vs.depends) do
1293                                 deps = _uvl_strip_remote_dependencies(deps)
1294                                 if next(deps) then
1295                                         self:depends(deps)
1296                                 end
1297                         end
1298                 end
1299         end
1300
1301         self.cast = self.cast or "string"
1302 end
1303
1304 -- Add a dependencie to another section field
1305 function AbstractValue.depends(self, field, value)
1306         local deps
1307         if type(field) == "string" then
1308                 deps = {}
1309                 deps[field] = value
1310         else
1311                 deps = field
1312         end
1313
1314         table.insert(self.deps, {deps=deps, add=""})
1315 end
1316
1317 -- Generates the unique CBID
1318 function AbstractValue.cbid(self, section)
1319         return "cbid."..self.map.config.."."..section.."."..self.option
1320 end
1321
1322 -- Return whether this object should be created
1323 function AbstractValue.formcreated(self, section)
1324         local key = "cbi.opt."..self.config.."."..section
1325         return (self.map:formvalue(key) == self.option)
1326 end
1327
1328 -- Returns the formvalue for this object
1329 function AbstractValue.formvalue(self, section)
1330         return self.map:formvalue(self:cbid(section))
1331 end
1332
1333 function AbstractValue.additional(self, value)
1334         self.optional = value
1335 end
1336
1337 function AbstractValue.mandatory(self, value)
1338         self.rmempty = not value
1339 end
1340
1341 function AbstractValue.parse(self, section, novld)
1342         local fvalue = self:formvalue(section)
1343         local cvalue = self:cfgvalue(section)
1344
1345         -- If favlue and cvalue are both tables and have the same content
1346         -- make them identical
1347         if type(fvalue) == "table" and type(cvalue) == "table" then
1348                 local equal = #fvalue == #cvalue
1349                 if equal then
1350                         for i=1, #fvalue do
1351                                 if cvalue[i] ~= fvalue[i] then
1352                                         equal = false
1353                                 end
1354                         end
1355                 end
1356                 if equal then
1357                         fvalue = cvalue
1358                 end
1359         end
1360
1361         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1362                 fvalue = self:transform(self:validate(fvalue, section))
1363                 if not fvalue and not novld then
1364                         if self.error then
1365                                 self.error[section] = "invalid"
1366                         else
1367                                 self.error = { [section] = "invalid" }
1368                         end
1369                         if self.section.error then
1370                                 table.insert(self.section.error[section], "invalid")
1371                         else
1372                                 self.section.error = {[section] = {"invalid"}}
1373                         end 
1374                         self.map.save = false
1375                 end
1376                 if fvalue and not (fvalue == cvalue) then
1377                         if self:write(section, fvalue) then
1378                                 -- Push events
1379                                 self.section.changed = true
1380                                 --luci.util.append(self.map.events, self.events)
1381                         end
1382                 end
1383         else                                                    -- Unset the UCI or error
1384                 if self.rmempty or self.optional then
1385                         if self:remove(section) then
1386                                 -- Push events
1387                                 self.section.changed = true
1388                                 --luci.util.append(self.map.events, self.events)
1389                         end
1390                 elseif cvalue ~= fvalue and not novld then
1391                         self:write(section, fvalue or "")
1392                         if self.error then
1393                                 self.error[section] = "missing"
1394                         else
1395                                 self.error = { [section] = "missing" }
1396                         end
1397                         self.map.save = false
1398                 end
1399         end
1400 end
1401
1402 -- Render if this value exists or if it is mandatory
1403 function AbstractValue.render(self, s, scope)
1404         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1405                 scope = scope or {}
1406                 scope.section   = s
1407                 scope.cbid      = self:cbid(s)
1408                 scope.striptags = luci.util.striptags
1409                 scope.pcdata    = luci.util.pcdata
1410
1411                 scope.ifattr = function(cond,key,val)
1412                         if cond then
1413                                 return string.format(
1414                                         ' %s="%s"', tostring(key),
1415                                         luci.util.pcdata(tostring( val
1416                                          or scope[key]
1417                                          or (type(self[key]) ~= "function" and self[key])
1418                                          or "" ))
1419                                 )
1420                         else
1421                                 return ''
1422                         end
1423                 end
1424
1425                 scope.attr = function(...)
1426                         return scope.ifattr( true, ... )
1427                 end
1428
1429                 Node.render(self, scope)
1430         end
1431 end
1432
1433 -- Return the UCI value of this object
1434 function AbstractValue.cfgvalue(self, section)
1435         local value = self.map:get(section, self.option)
1436         if not value then
1437                 return nil
1438         elseif not self.cast or self.cast == type(value) then
1439                 return value
1440         elseif self.cast == "string" then
1441                 if type(value) == "table" then
1442                         return value[1]
1443                 end
1444         elseif self.cast == "table" then
1445                 return luci.util.split(value, "%s+", nil, true)
1446         end
1447 end
1448
1449 -- Validate the form value
1450 function AbstractValue.validate(self, value)
1451         return value
1452 end
1453
1454 AbstractValue.transform = AbstractValue.validate
1455
1456
1457 -- Write to UCI
1458 function AbstractValue.write(self, section, value)
1459         return self.map:set(section, self.option, value)
1460 end
1461
1462 -- Remove from UCI
1463 function AbstractValue.remove(self, section)
1464         return self.map:del(section, self.option)
1465 end
1466
1467
1468
1469
1470 --[[
1471 Value - A one-line value
1472         maxlength:      The maximum length
1473 ]]--
1474 Value = class(AbstractValue)
1475
1476 function Value.__init__(self, ...)
1477         AbstractValue.__init__(self, ...)
1478         self.template  = "cbi/value"
1479         self.keylist = {}
1480         self.vallist = {}
1481 end
1482
1483 function Value.value(self, key, val)
1484         val = val or key
1485         table.insert(self.keylist, tostring(key))
1486         table.insert(self.vallist, tostring(val))
1487 end
1488
1489
1490 -- DummyValue - This does nothing except being there
1491 DummyValue = class(AbstractValue)
1492
1493 function DummyValue.__init__(self, ...)
1494         AbstractValue.__init__(self, ...)
1495         self.template = "cbi/dvalue"
1496         self.value = nil
1497 end
1498
1499 function DummyValue.cfgvalue(self, section)
1500         local value
1501         if self.value then
1502                 if type(self.value) == "function" then
1503                         value = self:value(section)
1504                 else
1505                         value = self.value
1506                 end
1507         else
1508                 value = AbstractValue.cfgvalue(self, section)
1509         end
1510         return value
1511 end
1512
1513 function DummyValue.parse(self)
1514
1515 end
1516
1517
1518 --[[
1519 Flag - A flag being enabled or disabled
1520 ]]--
1521 Flag = class(AbstractValue)
1522
1523 function Flag.__init__(self, ...)
1524         AbstractValue.__init__(self, ...)
1525         self.template  = "cbi/fvalue"
1526
1527         self.enabled = "1"
1528         self.disabled = "0"
1529 end
1530
1531 -- A flag can only have two states: set or unset
1532 function Flag.parse(self, section)
1533         local fvalue = self:formvalue(section)
1534
1535         if fvalue then
1536                 fvalue = self.enabled
1537         else
1538                 fvalue = self.disabled
1539         end
1540
1541         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1542                 if not(fvalue == self:cfgvalue(section)) then
1543                         self:write(section, fvalue)
1544                 end
1545         else
1546                 self:remove(section)
1547         end
1548 end
1549
1550
1551
1552 --[[
1553 ListValue - A one-line value predefined in a list
1554         widget: The widget that will be used (select, radio)
1555 ]]--
1556 ListValue = class(AbstractValue)
1557
1558 function ListValue.__init__(self, ...)
1559         AbstractValue.__init__(self, ...)
1560         self.template  = "cbi/lvalue"
1561
1562         self.keylist = {}
1563         self.vallist = {}
1564         self.size   = 1
1565         self.widget = "select"
1566 end
1567
1568 function ListValue.prepare(self, ...)
1569         AbstractValue.prepare(self, ...)
1570         if not self.override_scheme
1571          and self.map:get_scheme(self.section.sectiontype, self.option) then
1572                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1573                 if self.value and vs.valuelist and not self.override_values then
1574                         for k, v in ipairs(vs.valuelist) do
1575                                 local deps = {}
1576                                 if not self.override_dependencies
1577                                  and vs.enum_depends and vs.enum_depends[v.value] then
1578                                         for i, dep in ipairs(vs.enum_depends[v.value]) do
1579                                                 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1580                                         end
1581                                 end
1582                                 self:value(v.value, v.title or v.value, unpack(deps))
1583                         end
1584                 end
1585         end
1586 end
1587
1588 function ListValue.value(self, key, val, ...)
1589         if luci.util.contains(self.keylist, key) then
1590                 return
1591         end
1592
1593         val = val or key
1594         table.insert(self.keylist, tostring(key))
1595         table.insert(self.vallist, tostring(val))
1596
1597         for i, deps in ipairs({...}) do
1598                 table.insert(self.deps, {add = "-"..key, deps=deps})
1599         end
1600 end
1601
1602 function ListValue.validate(self, val)
1603         if luci.util.contains(self.keylist, val) then
1604                 return val
1605         else
1606                 return nil
1607         end
1608 end
1609
1610
1611
1612 --[[
1613 MultiValue - Multiple delimited values
1614         widget: The widget that will be used (select, checkbox)
1615         delimiter: The delimiter that will separate the values (default: " ")
1616 ]]--
1617 MultiValue = class(AbstractValue)
1618
1619 function MultiValue.__init__(self, ...)
1620         AbstractValue.__init__(self, ...)
1621         self.template = "cbi/mvalue"
1622
1623         self.keylist = {}
1624         self.vallist = {}
1625
1626         self.widget = "checkbox"
1627         self.delimiter = " "
1628 end
1629
1630 function MultiValue.render(self, ...)
1631         if self.widget == "select" and not self.size then
1632                 self.size = #self.vallist
1633         end
1634
1635         AbstractValue.render(self, ...)
1636 end
1637
1638 function MultiValue.value(self, key, val)
1639         if luci.util.contains(self.keylist, key) then
1640                 return
1641         end
1642
1643         val = val or key
1644         table.insert(self.keylist, tostring(key))
1645         table.insert(self.vallist, tostring(val))
1646 end
1647
1648 function MultiValue.valuelist(self, section)
1649         local val = self:cfgvalue(section)
1650
1651         if not(type(val) == "string") then
1652                 return {}
1653         end
1654
1655         return luci.util.split(val, self.delimiter)
1656 end
1657
1658 function MultiValue.validate(self, val)
1659         val = (type(val) == "table") and val or {val}
1660
1661         local result
1662
1663         for i, value in ipairs(val) do
1664                 if luci.util.contains(self.keylist, value) then
1665                         result = result and (result .. self.delimiter .. value) or value
1666                 end
1667         end
1668
1669         return result
1670 end
1671
1672
1673 StaticList = class(MultiValue)
1674
1675 function StaticList.__init__(self, ...)
1676         MultiValue.__init__(self, ...)
1677         self.cast = "table"
1678         self.valuelist = self.cfgvalue
1679
1680         if not self.override_scheme
1681          and self.map:get_scheme(self.section.sectiontype, self.option) then
1682                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1683                 if self.value and vs.values and not self.override_values then
1684                         for k, v in pairs(vs.values) do
1685                                 self:value(k, v)
1686                         end
1687                 end
1688         end
1689 end
1690
1691 function StaticList.validate(self, value)
1692         value = (type(value) == "table") and value or {value}
1693
1694         local valid = {}
1695         for i, v in ipairs(value) do
1696                 if luci.util.contains(self.keylist, v) then
1697                         table.insert(valid, v)
1698                 end
1699         end
1700         return valid
1701 end
1702
1703
1704 DynamicList = class(AbstractValue)
1705
1706 function DynamicList.__init__(self, ...)
1707         AbstractValue.__init__(self, ...)
1708         self.template  = "cbi/dynlist"
1709         self.cast = "table"
1710         self.keylist = {}
1711         self.vallist = {}
1712 end
1713
1714 function DynamicList.value(self, key, val)
1715         val = val or key
1716         table.insert(self.keylist, tostring(key))
1717         table.insert(self.vallist, tostring(val))
1718 end
1719
1720 function DynamicList.write(self, ...)
1721         self.map.proceed = true
1722         return AbstractValue.write(self, ...)
1723 end
1724
1725 function DynamicList.formvalue(self, section)
1726         local value = AbstractValue.formvalue(self, section)
1727         value = (type(value) == "table") and value or {value}
1728
1729         local valid = {}
1730         for i, v in ipairs(value) do
1731                 if v and #v > 0
1732                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1733                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1734                         table.insert(valid, v)
1735                 end
1736         end
1737
1738         return valid
1739 end
1740
1741
1742 --[[
1743 TextValue - A multi-line value
1744         rows:   Rows
1745 ]]--
1746 TextValue = class(AbstractValue)
1747
1748 function TextValue.__init__(self, ...)
1749         AbstractValue.__init__(self, ...)
1750         self.template  = "cbi/tvalue"
1751 end
1752
1753 --[[
1754 Button
1755 ]]--
1756 Button = class(AbstractValue)
1757
1758 function Button.__init__(self, ...)
1759         AbstractValue.__init__(self, ...)
1760         self.template  = "cbi/button"
1761         self.inputstyle = nil
1762         self.rmempty = true
1763 end
1764
1765
1766 FileUpload = class(AbstractValue)
1767
1768 function FileUpload.__init__(self, ...)
1769         AbstractValue.__init__(self, ...)
1770         self.template = "cbi/upload"
1771         if not self.map.upload_fields then
1772                 self.map.upload_fields = { self }
1773         else
1774                 self.map.upload_fields[#self.map.upload_fields+1] = self
1775         end
1776 end
1777
1778 function FileUpload.formcreated(self, section)
1779         return AbstractValue.formcreated(self, section) or
1780                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1781                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1782 end
1783
1784 function FileUpload.cfgvalue(self, section)
1785         local val = AbstractValue.cfgvalue(self, section)
1786         if val and fs.access(val) then
1787                 return val
1788         end
1789         return nil
1790 end
1791
1792 function FileUpload.formvalue(self, section)
1793         local val = AbstractValue.formvalue(self, section)
1794         if val then
1795                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1796                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1797                 then
1798                         return val
1799                 end
1800                 fs.unlink(val)
1801                 self.value = nil
1802         end
1803         return nil
1804 end
1805
1806 function FileUpload.remove(self, section)
1807         local val = AbstractValue.formvalue(self, section)
1808         if val and fs.access(val) then fs.unlink(val) end
1809         return AbstractValue.remove(self, section)
1810 end
1811
1812
1813 FileBrowser = class(AbstractValue)
1814
1815 function FileBrowser.__init__(self, ...)
1816         AbstractValue.__init__(self, ...)
1817         self.template = "cbi/browser"
1818 end