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