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