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