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