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