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