all: remove references to old i18n files
[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:submitstate() then
675                 Node.parse(self, 1, ...)
676         end
677
678         local valid = true
679         for k, j in ipairs(self.children) do
680                 for i, v in ipairs(j.children) do
681                         valid = valid
682                          and (not v.tag_missing or not v.tag_missing[1])
683                          and (not v.tag_invalid or not v.tag_invalid[1])
684                          and (not v.error)
685                 end
686         end
687
688         local state =
689                 not self:submitstate() and FORM_NODATA
690                 or valid and FORM_VALID
691                 or FORM_INVALID
692
693         self.dorender = not self.handle
694         if self.handle then
695                 local nrender, nstate = self:handle(state, self.data)
696                 self.dorender = self.dorender or (nrender ~= false)
697                 state = nstate or state
698         end
699         return state
700 end
701
702 function SimpleForm.render(self, ...)
703         if self.dorender then
704                 Node.render(self, ...)
705         end
706 end
707
708 function SimpleForm.submitstate(self)
709         return self:formvalue("cbi.submit")
710 end
711
712 function SimpleForm.section(self, class, ...)
713         if instanceof(class, AbstractSection) then
714                 local obj  = class(self, ...)
715                 self:append(obj)
716                 return obj
717         else
718                 error("class must be a descendent of AbstractSection")
719         end
720 end
721
722 -- Creates a child field
723 function SimpleForm.field(self, class, ...)
724         local section
725         for k, v in ipairs(self.children) do
726                 if instanceof(v, SimpleSection) then
727                         section = v
728                         break
729                 end
730         end
731         if not section then
732                 section = self:section(SimpleSection)
733         end
734
735         if instanceof(class, AbstractValue) then
736                 local obj  = class(self, section, ...)
737                 obj.track_missing = true
738                 section:append(obj)
739                 return obj
740         else
741                 error("class must be a descendent of AbstractValue")
742         end
743 end
744
745 function SimpleForm.set(self, section, option, value)
746         self.data[option] = value
747 end
748
749
750 function SimpleForm.del(self, section, option)
751         self.data[option] = nil
752 end
753
754
755 function SimpleForm.get(self, section, option)
756         return self.data[option]
757 end
758
759
760 function SimpleForm.get_scheme()
761         return nil
762 end
763
764
765 Form = class(SimpleForm)
766
767 function Form.__init__(self, ...)
768         SimpleForm.__init__(self, ...)
769         self.embedded = true
770 end
771
772
773 --[[
774 AbstractSection
775 ]]--
776 AbstractSection = class(Node)
777
778 function AbstractSection.__init__(self, map, sectiontype, ...)
779         Node.__init__(self, ...)
780         self.sectiontype = sectiontype
781         self.map = map
782         self.config = map.config
783         self.optionals = {}
784         self.defaults = {}
785         self.fields = {}
786         self.tag_error = {}
787         self.tag_invalid = {}
788         self.tag_deperror = {}
789         self.changed = false
790
791         self.optional = true
792         self.addremove = false
793         self.dynamic = false
794 end
795
796 -- Define a tab for the section
797 function AbstractSection.tab(self, tab, title, desc)
798         self.tabs      = self.tabs      or { }
799         self.tab_names = self.tab_names or { }
800
801         self.tab_names[#self.tab_names+1] = tab
802         self.tabs[tab] = {
803                 title       = title,
804                 description = desc,
805                 childs      = { }
806         }
807 end
808
809 -- Appends a new option
810 function AbstractSection.option(self, class, option, ...)
811         -- Autodetect from UVL
812         if class == true and self.map:get_scheme(self.sectiontype, option) then
813                 local vs = self.map:get_scheme(self.sectiontype, option)
814                 if vs.type == "boolean" then
815                         class = Flag
816                 elseif vs.type == "list" then
817                         class = DynamicList
818                 elseif vs.type == "enum" or vs.type == "reference" then
819                         class = ListValue
820                 else
821                         class = Value
822                 end
823         end
824
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         for _, node in ipairs(self.tabs[tab].childs) do
858                 node:render(...)
859         end
860 end
861
862 -- Parse optional options
863 function AbstractSection.parse_optionals(self, section)
864         if not self.optional then
865                 return
866         end
867
868         self.optionals[section] = {}
869
870         local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
871         for k,v in ipairs(self.children) do
872                 if v.optional and not v:cfgvalue(section) then
873                         if field == v.option then
874                                 field = nil
875                                 self.map.proceed = true
876                         else
877                                 table.insert(self.optionals[section], v)
878                         end
879                 end
880         end
881
882         if field and #field > 0 and self.dynamic then
883                 self:add_dynamic(field)
884         end
885 end
886
887 -- Add a dynamic option
888 function AbstractSection.add_dynamic(self, field, optional)
889         local o = self:option(Value, field, field)
890         o.optional = optional
891 end
892
893 -- Parse all dynamic options
894 function AbstractSection.parse_dynamic(self, section)
895         if not self.dynamic then
896                 return
897         end
898
899         local arr  = luci.util.clone(self:cfgvalue(section))
900         local form = self.map:formvaluetable("cbid."..self.config.."."..section)
901         for k, v in pairs(form) do
902                 arr[k] = v
903         end
904
905         for key,val in pairs(arr) do
906                 local create = true
907
908                 for i,c in ipairs(self.children) do
909                         if c.option == key then
910                                 create = false
911                         end
912                 end
913
914                 if create and key:sub(1, 1) ~= "." then
915                         self.map.proceed = true
916                         self:add_dynamic(key, true)
917                 end
918         end
919 end
920
921 -- Returns the section's UCI table
922 function AbstractSection.cfgvalue(self, section)
923         return self.map:get(section)
924 end
925
926 -- Push events
927 function AbstractSection.push_events(self)
928         --luci.util.append(self.map.events, self.events)
929         self.map.changed = true
930 end
931
932 -- Removes the section
933 function AbstractSection.remove(self, section)
934         self.map.proceed = true
935         return self.map:del(section)
936 end
937
938 -- Creates the section
939 function AbstractSection.create(self, section)
940         local stat
941
942         if section then
943                 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
944         else
945                 section = self.map:add(self.sectiontype)
946                 stat = section
947         end
948
949         if stat then
950                 for k,v in pairs(self.children) do
951                         if v.default then
952                                 self.map:set(section, v.option, v.default)
953                         end
954                 end
955
956                 for k,v in pairs(self.defaults) do
957                         self.map:set(section, k, v)
958                 end
959         end
960
961         self.map.proceed = true
962
963         return stat
964 end
965
966
967 SimpleSection = class(AbstractSection)
968
969 function SimpleSection.__init__(self, form, ...)
970         AbstractSection.__init__(self, form, nil, ...)
971         self.template = "cbi/nullsection"
972 end
973
974
975 Table = class(AbstractSection)
976
977 function Table.__init__(self, form, data, ...)
978         local datasource = {}
979         local tself = self
980         datasource.config = "table"
981         self.data = data or {}
982
983         datasource.formvalue = Map.formvalue
984         datasource.formvaluetable = Map.formvaluetable
985         datasource.readinput = true
986
987         function datasource.get(self, section, option)
988                 return tself.data[section] and tself.data[section][option]
989         end
990
991         function datasource.submitstate(self)
992                 return Map.formvalue(self, "cbi.submit")
993         end
994
995         function datasource.del(...)
996                 return true
997         end
998
999         function datasource.get_scheme()
1000                 return nil
1001         end
1002
1003         AbstractSection.__init__(self, datasource, "table", ...)
1004         self.template = "cbi/tblsection"
1005         self.rowcolors = true
1006         self.anonymous = true
1007 end
1008
1009 function Table.parse(self, readinput)
1010         self.map.readinput = (readinput ~= false)
1011         for i, k in ipairs(self:cfgsections()) do
1012                 if self.map:submitstate() then
1013                         Node.parse(self, k)
1014                 end
1015         end
1016 end
1017
1018 function Table.cfgsections(self)
1019         local sections = {}
1020
1021         for i, v in luci.util.kspairs(self.data) do
1022                 table.insert(sections, i)
1023         end
1024
1025         return sections
1026 end
1027
1028 function Table.update(self, data)
1029         self.data = data
1030 end
1031
1032
1033
1034 --[[
1035 NamedSection - A fixed configuration section defined by its name
1036 ]]--
1037 NamedSection = class(AbstractSection)
1038
1039 function NamedSection.__init__(self, map, section, stype, ...)
1040         AbstractSection.__init__(self, map, stype, ...)
1041
1042         -- Defaults
1043         self.addremove = false
1044
1045         -- Use defaults from UVL
1046         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1047                 local vs = self.map:get_scheme(self.sectiontype)
1048                 self.addremove = not vs.unique and not vs.required
1049                 self.dynamic   = vs.dynamic
1050                 self.title       = self.title or vs.title
1051                 self.description = self.description or vs.descr
1052         end
1053
1054         self.template = "cbi/nsection"
1055         self.section = section
1056 end
1057
1058 function NamedSection.parse(self, novld)
1059         local s = self.section
1060         local active = self:cfgvalue(s)
1061
1062         if self.addremove then
1063                 local path = self.config.."."..s
1064                 if active then -- Remove the section
1065                         if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1066                                 self:push_events()
1067                                 return
1068                         end
1069                 else           -- Create and apply default values
1070                         if self.map:formvalue("cbi.cns."..path) then
1071                                 self:create(s)
1072                                 return
1073                         end
1074                 end
1075         end
1076
1077         if active then
1078                 AbstractSection.parse_dynamic(self, s)
1079                 if self.map:submitstate() then
1080                         Node.parse(self, s)
1081
1082                         if not novld and not self.override_scheme and self.map.scheme then
1083                                 _uvl_validate_section(self, s)
1084                         end
1085                 end
1086                 AbstractSection.parse_optionals(self, s)
1087
1088                 if self.changed then
1089                         self:push_events()
1090                 end
1091         end
1092 end
1093
1094
1095 --[[
1096 TypedSection - A (set of) configuration section(s) defined by the type
1097         addremove:      Defines whether the user can add/remove sections of this type
1098         anonymous:  Allow creating anonymous sections
1099         validate:       a validation function returning nil if the section is invalid
1100 ]]--
1101 TypedSection = class(AbstractSection)
1102
1103 function TypedSection.__init__(self, map, type, ...)
1104         AbstractSection.__init__(self, map, type, ...)
1105
1106         self.template  = "cbi/tsection"
1107         self.deps = {}
1108         self.anonymous = false
1109
1110         -- Use defaults from UVL
1111         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1112                 local vs = self.map:get_scheme(self.sectiontype)
1113                 self.addremove = not vs.unique and not vs.required
1114                 self.dynamic   = vs.dynamic
1115                 self.anonymous = not vs.named
1116                 self.title       = self.title or vs.title
1117                 self.description = self.description or vs.descr
1118         end
1119 end
1120
1121 -- Return all matching UCI sections for this TypedSection
1122 function TypedSection.cfgsections(self)
1123         local sections = {}
1124         self.map.uci:foreach(self.map.config, self.sectiontype,
1125                 function (section)
1126                         if self:checkscope(section[".name"]) then
1127                                 table.insert(sections, section[".name"])
1128                         end
1129                 end)
1130
1131         return sections
1132 end
1133
1134 -- Limits scope to sections that have certain option => value pairs
1135 function TypedSection.depends(self, option, value)
1136         table.insert(self.deps, {option=option, value=value})
1137 end
1138
1139 function TypedSection.parse(self, novld)
1140         if self.addremove then
1141                 -- Remove
1142                 local crval = REMOVE_PREFIX .. self.config
1143                 local name = self.map:formvaluetable(crval)
1144                 for k,v in pairs(name) do
1145                         if k:sub(-2) == ".x" then
1146                                 k = k:sub(1, #k - 2)
1147                         end
1148                         if self:cfgvalue(k) and self:checkscope(k) then
1149                                 self:remove(k)
1150                         end
1151                 end
1152         end
1153
1154         local co
1155         for i, k in ipairs(self:cfgsections()) do
1156                 AbstractSection.parse_dynamic(self, k)
1157                 if self.map:submitstate() then
1158                         Node.parse(self, k, novld)
1159
1160                         if not novld and not self.override_scheme and self.map.scheme then
1161                                 _uvl_validate_section(self, k)
1162                         end
1163                 end
1164                 AbstractSection.parse_optionals(self, k)
1165         end
1166
1167         if self.addremove then
1168                 -- Create
1169                 local created
1170                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1171                 local name  = self.map:formvalue(crval)
1172                 if self.anonymous then
1173                         if name then
1174                                 created = self:create()
1175                         end
1176                 else
1177                         if name then
1178                                 -- Ignore if it already exists
1179                                 if self:cfgvalue(name) then
1180                                         name = nil;
1181                                 end
1182
1183                                 name = self:checkscope(name)
1184
1185                                 if not name then
1186                                         self.err_invalid = true
1187                                 end
1188
1189                                 if name and #name > 0 then
1190                                         created = self:create(name) and name
1191                                         if not created then
1192                                                 self.invalid_cts = true
1193                                         end
1194                                 end
1195                         end
1196                 end
1197
1198                 if created then
1199                         AbstractSection.parse_optionals(self, created)
1200                 end
1201         end
1202
1203         if created or self.changed then
1204                 self:push_events()
1205         end
1206 end
1207
1208 -- Verifies scope of sections
1209 function TypedSection.checkscope(self, section)
1210         -- Check if we are not excluded
1211         if self.filter and not self:filter(section) then
1212                 return nil
1213         end
1214
1215         -- Check if at least one dependency is met
1216         if #self.deps > 0 and self:cfgvalue(section) then
1217                 local stat = false
1218
1219                 for k, v in ipairs(self.deps) do
1220                         if self:cfgvalue(section)[v.option] == v.value then
1221                                 stat = true
1222                         end
1223                 end
1224
1225                 if not stat then
1226                         return nil
1227                 end
1228         end
1229
1230         return self:validate(section)
1231 end
1232
1233
1234 -- Dummy validate function
1235 function TypedSection.validate(self, section)
1236         return section
1237 end
1238
1239
1240 --[[
1241 AbstractValue - An abstract Value Type
1242         null:           Value can be empty
1243         valid:          A function returning the value if it is valid otherwise nil
1244         depends:        A table of option => value pairs of which one must be true
1245         default:        The default value
1246         size:           The size of the input fields
1247         rmempty:        Unset value if empty
1248         optional:       This value is optional (see AbstractSection.optionals)
1249 ]]--
1250 AbstractValue = class(Node)
1251
1252 function AbstractValue.__init__(self, map, section, option, ...)
1253         Node.__init__(self, ...)
1254         self.section = section
1255         self.option  = option
1256         self.map     = map
1257         self.config  = map.config
1258         self.tag_invalid = {}
1259         self.tag_missing = {}
1260         self.tag_reqerror = {}
1261         self.tag_error = {}
1262         self.deps = {}
1263         --self.cast = "string"
1264
1265         self.track_missing = false
1266         self.rmempty   = true
1267         self.default   = nil
1268         self.size      = nil
1269         self.optional  = false
1270 end
1271
1272 function AbstractValue.prepare(self)
1273         -- Use defaults from UVL
1274         if not self.override_scheme
1275          and self.map:get_scheme(self.section.sectiontype, self.option) then
1276                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1277                 if self.cast == nil then
1278                         self.cast = (vs.type == "list") and "list" or "string"
1279                 end
1280                 self.title       = self.title or vs.title
1281                 self.description = self.description or vs.descr
1282                 if self.default == nil then
1283                         self.default = vs.default
1284                 end
1285
1286                 if vs.depends and not self.override_dependencies then
1287                         for i, deps in ipairs(vs.depends) do
1288                                 deps = _uvl_strip_remote_dependencies(deps)
1289                                 if next(deps) then
1290                                         self:depends(deps)
1291                                 end
1292                         end
1293                 end
1294         end
1295
1296         self.cast = self.cast or "string"
1297 end
1298
1299 -- Add a dependencie to another section field
1300 function AbstractValue.depends(self, field, value)
1301         local deps
1302         if type(field) == "string" then
1303                 deps = {}
1304                 deps[field] = value
1305         else
1306                 deps = field
1307         end
1308
1309         table.insert(self.deps, {deps=deps, add=""})
1310 end
1311
1312 -- Generates the unique CBID
1313 function AbstractValue.cbid(self, section)
1314         return "cbid."..self.map.config.."."..section.."."..self.option
1315 end
1316
1317 -- Return whether this object should be created
1318 function AbstractValue.formcreated(self, section)
1319         local key = "cbi.opt."..self.config.."."..section
1320         return (self.map:formvalue(key) == self.option)
1321 end
1322
1323 -- Returns the formvalue for this object
1324 function AbstractValue.formvalue(self, section)
1325         return self.map:formvalue(self:cbid(section))
1326 end
1327
1328 function AbstractValue.additional(self, value)
1329         self.optional = value
1330 end
1331
1332 function AbstractValue.mandatory(self, value)
1333         self.rmempty = not value
1334 end
1335
1336 function AbstractValue.parse(self, section, novld)
1337         local fvalue = self:formvalue(section)
1338         local cvalue = self:cfgvalue(section)
1339
1340         -- If favlue and cvalue are both tables and have the same content
1341         -- make them identical
1342         if type(fvalue) == "table" and type(cvalue) == "table" then
1343                 local equal = #fvalue == #cvalue
1344                 if equal then
1345                         for i=1, #fvalue do
1346                                 if cvalue[i] ~= fvalue[i] then
1347                                         equal = false
1348                                 end
1349                         end
1350                 end
1351                 if equal then
1352                         fvalue = cvalue
1353                 end
1354         end
1355
1356         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1357                 fvalue = self:transform(self:validate(fvalue, section))
1358                 if not fvalue and not novld then
1359                         if self.error then
1360                                 self.error[section] = "invalid"
1361                         else
1362                                 self.error = { [section] = "invalid" }
1363                         end
1364                         if self.section.error then
1365                                 table.insert(self.section.error[section], "invalid")
1366                         else
1367                                 self.section.error = {[section] = {"invalid"}}
1368                         end 
1369                         self.map.save = false
1370                 end
1371                 if fvalue and not (fvalue == cvalue) then
1372                         if self:write(section, fvalue) then
1373                                 -- Push events
1374                                 self.section.changed = true
1375                                 --luci.util.append(self.map.events, self.events)
1376                         end
1377                 end
1378         else                                                    -- Unset the UCI or error
1379                 if self.rmempty or self.optional then
1380                         if self:remove(section) then
1381                                 -- Push events
1382                                 self.section.changed = true
1383                                 --luci.util.append(self.map.events, self.events)
1384                         end
1385                 elseif cvalue ~= fvalue and not novld then
1386                         self:write(section, fvalue or "")
1387                         if self.error then
1388                                 self.error[section] = "missing"
1389                         else
1390                                 self.error = { [section] = "missing" }
1391                         end
1392                         self.map.save = false
1393                 end
1394         end
1395 end
1396
1397 -- Render if this value exists or if it is mandatory
1398 function AbstractValue.render(self, s, scope)
1399         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1400                 scope = scope or {}
1401                 scope.section   = s
1402                 scope.cbid      = self:cbid(s)
1403                 scope.striptags = luci.util.striptags
1404                 scope.pcdata    = luci.util.pcdata
1405
1406                 scope.ifattr = function(cond,key,val)
1407                         if cond then
1408                                 return string.format(
1409                                         ' %s="%s"', tostring(key),
1410                                         luci.util.pcdata(tostring( val
1411                                          or scope[key]
1412                                          or (type(self[key]) ~= "function" and self[key])
1413                                          or "" ))
1414                                 )
1415                         else
1416                                 return ''
1417                         end
1418                 end
1419
1420                 scope.attr = function(...)
1421                         return scope.ifattr( true, ... )
1422                 end
1423
1424                 Node.render(self, scope)
1425         end
1426 end
1427
1428 -- Return the UCI value of this object
1429 function AbstractValue.cfgvalue(self, section)
1430         local value = self.map:get(section, self.option)
1431         if not value then
1432                 return nil
1433         elseif not self.cast or self.cast == type(value) then
1434                 return value
1435         elseif self.cast == "string" then
1436                 if type(value) == "table" then
1437                         return value[1]
1438                 end
1439         elseif self.cast == "table" then
1440                 return luci.util.split(value, "%s+", nil, true)
1441         end
1442 end
1443
1444 -- Validate the form value
1445 function AbstractValue.validate(self, value)
1446         return value
1447 end
1448
1449 AbstractValue.transform = AbstractValue.validate
1450
1451
1452 -- Write to UCI
1453 function AbstractValue.write(self, section, value)
1454         return self.map:set(section, self.option, value)
1455 end
1456
1457 -- Remove from UCI
1458 function AbstractValue.remove(self, section)
1459         return self.map:del(section, self.option)
1460 end
1461
1462
1463
1464
1465 --[[
1466 Value - A one-line value
1467         maxlength:      The maximum length
1468 ]]--
1469 Value = class(AbstractValue)
1470
1471 function Value.__init__(self, ...)
1472         AbstractValue.__init__(self, ...)
1473         self.template  = "cbi/value"
1474         self.keylist = {}
1475         self.vallist = {}
1476 end
1477
1478 function Value.value(self, key, val)
1479         val = val or key
1480         table.insert(self.keylist, tostring(key))
1481         table.insert(self.vallist, tostring(val))
1482 end
1483
1484
1485 -- DummyValue - This does nothing except being there
1486 DummyValue = class(AbstractValue)
1487
1488 function DummyValue.__init__(self, ...)
1489         AbstractValue.__init__(self, ...)
1490         self.template = "cbi/dvalue"
1491         self.value = nil
1492 end
1493
1494 function DummyValue.cfgvalue(self, section)
1495         local value
1496         if self.value then
1497                 if type(self.value) == "function" then
1498                         value = self:value(section)
1499                 else
1500                         value = self.value
1501                 end
1502         else
1503                 value = AbstractValue.cfgvalue(self, section)
1504         end
1505         return value
1506 end
1507
1508 function DummyValue.parse(self)
1509
1510 end
1511
1512
1513 --[[
1514 Flag - A flag being enabled or disabled
1515 ]]--
1516 Flag = class(AbstractValue)
1517
1518 function Flag.__init__(self, ...)
1519         AbstractValue.__init__(self, ...)
1520         self.template  = "cbi/fvalue"
1521
1522         self.enabled = "1"
1523         self.disabled = "0"
1524 end
1525
1526 -- A flag can only have two states: set or unset
1527 function Flag.parse(self, section)
1528         local fvalue = self:formvalue(section)
1529
1530         if fvalue then
1531                 fvalue = self.enabled
1532         else
1533                 fvalue = self.disabled
1534         end
1535
1536         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1537                 if not(fvalue == self:cfgvalue(section)) then
1538                         self:write(section, fvalue)
1539                 end
1540         else
1541                 self:remove(section)
1542         end
1543 end
1544
1545
1546
1547 --[[
1548 ListValue - A one-line value predefined in a list
1549         widget: The widget that will be used (select, radio)
1550 ]]--
1551 ListValue = class(AbstractValue)
1552
1553 function ListValue.__init__(self, ...)
1554         AbstractValue.__init__(self, ...)
1555         self.template  = "cbi/lvalue"
1556
1557         self.keylist = {}
1558         self.vallist = {}
1559         self.size   = 1
1560         self.widget = "select"
1561 end
1562
1563 function ListValue.prepare(self, ...)
1564         AbstractValue.prepare(self, ...)
1565         if not self.override_scheme
1566          and self.map:get_scheme(self.section.sectiontype, self.option) then
1567                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1568                 if self.value and vs.valuelist and not self.override_values then
1569                         for k, v in ipairs(vs.valuelist) do
1570                                 local deps = {}
1571                                 if not self.override_dependencies
1572                                  and vs.enum_depends and vs.enum_depends[v.value] then
1573                                         for i, dep in ipairs(vs.enum_depends[v.value]) do
1574                                                 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1575                                         end
1576                                 end
1577                                 self:value(v.value, v.title or v.value, unpack(deps))
1578                         end
1579                 end
1580         end
1581 end
1582
1583 function ListValue.value(self, key, val, ...)
1584         if luci.util.contains(self.keylist, key) then
1585                 return
1586         end
1587
1588         val = val or key
1589         table.insert(self.keylist, tostring(key))
1590         table.insert(self.vallist, tostring(val))
1591
1592         for i, deps in ipairs({...}) do
1593                 table.insert(self.deps, {add = "-"..key, deps=deps})
1594         end
1595 end
1596
1597 function ListValue.validate(self, val)
1598         if luci.util.contains(self.keylist, val) then
1599                 return val
1600         else
1601                 return nil
1602         end
1603 end
1604
1605
1606
1607 --[[
1608 MultiValue - Multiple delimited values
1609         widget: The widget that will be used (select, checkbox)
1610         delimiter: The delimiter that will separate the values (default: " ")
1611 ]]--
1612 MultiValue = class(AbstractValue)
1613
1614 function MultiValue.__init__(self, ...)
1615         AbstractValue.__init__(self, ...)
1616         self.template = "cbi/mvalue"
1617
1618         self.keylist = {}
1619         self.vallist = {}
1620
1621         self.widget = "checkbox"
1622         self.delimiter = " "
1623 end
1624
1625 function MultiValue.render(self, ...)
1626         if self.widget == "select" and not self.size then
1627                 self.size = #self.vallist
1628         end
1629
1630         AbstractValue.render(self, ...)
1631 end
1632
1633 function MultiValue.value(self, key, val)
1634         if luci.util.contains(self.keylist, key) then
1635                 return
1636         end
1637
1638         val = val or key
1639         table.insert(self.keylist, tostring(key))
1640         table.insert(self.vallist, tostring(val))
1641 end
1642
1643 function MultiValue.valuelist(self, section)
1644         local val = self:cfgvalue(section)
1645
1646         if not(type(val) == "string") then
1647                 return {}
1648         end
1649
1650         return luci.util.split(val, self.delimiter)
1651 end
1652
1653 function MultiValue.validate(self, val)
1654         val = (type(val) == "table") and val or {val}
1655
1656         local result
1657
1658         for i, value in ipairs(val) do
1659                 if luci.util.contains(self.keylist, value) then
1660                         result = result and (result .. self.delimiter .. value) or value
1661                 end
1662         end
1663
1664         return result
1665 end
1666
1667
1668 StaticList = class(MultiValue)
1669
1670 function StaticList.__init__(self, ...)
1671         MultiValue.__init__(self, ...)
1672         self.cast = "table"
1673         self.valuelist = self.cfgvalue
1674
1675         if not self.override_scheme
1676          and self.map:get_scheme(self.section.sectiontype, self.option) then
1677                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1678                 if self.value and vs.values and not self.override_values then
1679                         for k, v in pairs(vs.values) do
1680                                 self:value(k, v)
1681                         end
1682                 end
1683         end
1684 end
1685
1686 function StaticList.validate(self, value)
1687         value = (type(value) == "table") and value or {value}
1688
1689         local valid = {}
1690         for i, v in ipairs(value) do
1691                 if luci.util.contains(self.keylist, v) then
1692                         table.insert(valid, v)
1693                 end
1694         end
1695         return valid
1696 end
1697
1698
1699 DynamicList = class(AbstractValue)
1700
1701 function DynamicList.__init__(self, ...)
1702         AbstractValue.__init__(self, ...)
1703         self.template  = "cbi/dynlist"
1704         self.cast = "table"
1705         self.keylist = {}
1706         self.vallist = {}
1707 end
1708
1709 function DynamicList.value(self, key, val)
1710         val = val or key
1711         table.insert(self.keylist, tostring(key))
1712         table.insert(self.vallist, tostring(val))
1713 end
1714
1715 function DynamicList.write(self, ...)
1716         self.map.proceed = true
1717         return AbstractValue.write(self, ...)
1718 end
1719
1720 function DynamicList.formvalue(self, section)
1721         local value = AbstractValue.formvalue(self, section)
1722         value = (type(value) == "table") and value or {value}
1723
1724         local valid = {}
1725         for i, v in ipairs(value) do
1726                 if v and #v > 0
1727                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1728                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1729                         table.insert(valid, v)
1730                 end
1731         end
1732
1733         return valid
1734 end
1735
1736
1737 --[[
1738 TextValue - A multi-line value
1739         rows:   Rows
1740 ]]--
1741 TextValue = class(AbstractValue)
1742
1743 function TextValue.__init__(self, ...)
1744         AbstractValue.__init__(self, ...)
1745         self.template  = "cbi/tvalue"
1746 end
1747
1748 --[[
1749 Button
1750 ]]--
1751 Button = class(AbstractValue)
1752
1753 function Button.__init__(self, ...)
1754         AbstractValue.__init__(self, ...)
1755         self.template  = "cbi/button"
1756         self.inputstyle = nil
1757         self.rmempty = true
1758 end
1759
1760
1761 FileUpload = class(AbstractValue)
1762
1763 function FileUpload.__init__(self, ...)
1764         AbstractValue.__init__(self, ...)
1765         self.template = "cbi/upload"
1766         if not self.map.upload_fields then
1767                 self.map.upload_fields = { self }
1768         else
1769                 self.map.upload_fields[#self.map.upload_fields+1] = self
1770         end
1771 end
1772
1773 function FileUpload.formcreated(self, section)
1774         return AbstractValue.formcreated(self, section) or
1775                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1776                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1777 end
1778
1779 function FileUpload.cfgvalue(self, section)
1780         local val = AbstractValue.cfgvalue(self, section)
1781         if val and fs.access(val) then
1782                 return val
1783         end
1784         return nil
1785 end
1786
1787 function FileUpload.formvalue(self, section)
1788         local val = AbstractValue.formvalue(self, section)
1789         if val then
1790                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1791                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1792                 then
1793                         return val
1794                 end
1795                 fs.unlink(val)
1796                 self.value = nil
1797         end
1798         return nil
1799 end
1800
1801 function FileUpload.remove(self, section)
1802         local val = AbstractValue.formvalue(self, section)
1803         if val and fs.access(val) then fs.unlink(val) end
1804         return AbstractValue.remove(self, section)
1805 end
1806
1807
1808 FileBrowser = class(AbstractValue)
1809
1810 function FileBrowser.__init__(self, ...)
1811         AbstractValue.__init__(self, ...)
1812         self.template = "cbi/browser"
1813 end