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