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