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