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