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