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