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