cc22229fcad1fc5d39f649a5c02696f41799a795
[project/luci.git] / libs / cbi / luasrc / cbi.lua
1 --[[
2 LuCI - Configuration Bind Interface
3
4 Description:
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
7
8 FileId:
9 $Id$
10
11 License:
12 Copyright 2008 Steven Barth <steven@midlink.org>
13
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at
17
18         http://www.apache.org/licenses/LICENSE-2.0
19
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
25
26 ]]--
27 module("luci.cbi", package.seeall)
28
29 require("luci.template")
30 local util = require("luci.util")
31 require("luci.http")
32
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 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 -- Appends a new option
768 function AbstractSection.option(self, class, option, ...)
769         if instanceof(class, AbstractValue) then
770                 local obj  = class(self.map, self, option, ...)
771                 self:append(obj)
772                 self.fields[option] = obj
773                 return obj
774         elseif class == true then
775                 error("No valid class was given and autodetection failed.")
776         else
777                 error("class must be a descendant of AbstractValue")
778         end
779 end
780
781 -- Appends a new tabbed option
782 function AbstractSection.taboption(self, tab, ...)
783
784         assert(tab and self.tabs and self.tabs[tab],
785                 "Cannot assign option to not existing tab %q" % tostring(tab))
786
787         local l = self.tabs[tab].childs
788         local o = AbstractSection.option(self, ...)
789
790         if o then l[#l+1] = o end
791
792         return o
793 end
794
795 -- Render a single tab
796 function AbstractSection.render_tab(self, tab, ...)
797
798         assert(tab and self.tabs and self.tabs[tab],
799                 "Cannot render not existing tab %q" % tostring(tab))
800
801         for _, node in ipairs(self.tabs[tab].childs) do
802                 node:render(...)
803         end
804 end
805
806 -- Parse optional options
807 function AbstractSection.parse_optionals(self, section)
808         if not self.optional then
809                 return
810         end
811
812         self.optionals[section] = {}
813
814         local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
815         for k,v in ipairs(self.children) do
816                 if v.optional and not v:cfgvalue(section) and not next(self.tabs) then
817                         if field == v.option then
818                                 field = nil
819                                 self.map.proceed = true
820                         else
821                                 table.insert(self.optionals[section], v)
822                         end
823                 end
824         end
825
826         if field and #field > 0 and self.dynamic then
827                 self:add_dynamic(field)
828         end
829 end
830
831 -- Add a dynamic option
832 function AbstractSection.add_dynamic(self, field, optional)
833         local o = self:option(Value, field, field)
834         o.optional = optional
835 end
836
837 -- Parse all dynamic options
838 function AbstractSection.parse_dynamic(self, section)
839         if not self.dynamic then
840                 return
841         end
842
843         local arr  = luci.util.clone(self:cfgvalue(section))
844         local form = self.map:formvaluetable("cbid."..self.config.."."..section)
845         for k, v in pairs(form) do
846                 arr[k] = v
847         end
848
849         for key,val in pairs(arr) do
850                 local create = true
851
852                 for i,c in ipairs(self.children) do
853                         if c.option == key then
854                                 create = false
855                         end
856                 end
857
858                 if create and key:sub(1, 1) ~= "." then
859                         self.map.proceed = true
860                         self:add_dynamic(key, true)
861                 end
862         end
863 end
864
865 -- Returns the section's UCI table
866 function AbstractSection.cfgvalue(self, section)
867         return self.map:get(section)
868 end
869
870 -- Push events
871 function AbstractSection.push_events(self)
872         --luci.util.append(self.map.events, self.events)
873         self.map.changed = true
874 end
875
876 -- Removes the section
877 function AbstractSection.remove(self, section)
878         self.map.proceed = true
879         return self.map:del(section)
880 end
881
882 -- Creates the section
883 function AbstractSection.create(self, section)
884         local stat
885
886         if section then
887                 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
888         else
889                 section = self.map:add(self.sectiontype)
890                 stat = section
891         end
892
893         if stat then
894                 for k,v in pairs(self.children) do
895                         if v.default then
896                                 self.map:set(section, v.option, v.default)
897                         end
898                 end
899
900                 for k,v in pairs(self.defaults) do
901                         self.map:set(section, k, v)
902                 end
903         end
904
905         self.map.proceed = true
906
907         return stat
908 end
909
910
911 SimpleSection = class(AbstractSection)
912
913 function SimpleSection.__init__(self, form, ...)
914         AbstractSection.__init__(self, form, nil, ...)
915         self.template = "cbi/nullsection"
916 end
917
918
919 Table = class(AbstractSection)
920
921 function Table.__init__(self, form, data, ...)
922         local datasource = {}
923         local tself = self
924         datasource.config = "table"
925         self.data = data or {}
926
927         datasource.formvalue = Map.formvalue
928         datasource.formvaluetable = Map.formvaluetable
929         datasource.readinput = true
930
931         function datasource.get(self, section, option)
932                 return tself.data[section] and tself.data[section][option]
933         end
934
935         function datasource.submitstate(self)
936                 return Map.formvalue(self, "cbi.submit")
937         end
938
939         function datasource.del(...)
940                 return true
941         end
942
943         function datasource.get_scheme()
944                 return nil
945         end
946
947         AbstractSection.__init__(self, datasource, "table", ...)
948         self.template = "cbi/tblsection"
949         self.rowcolors = true
950         self.anonymous = true
951 end
952
953 function Table.parse(self, readinput)
954         self.map.readinput = (readinput ~= false)
955         for i, k in ipairs(self:cfgsections()) do
956                 if self.map:submitstate() then
957                         Node.parse(self, k)
958                 end
959         end
960 end
961
962 function Table.cfgsections(self)
963         local sections = {}
964
965         for i, v in luci.util.kspairs(self.data) do
966                 table.insert(sections, i)
967         end
968
969         return sections
970 end
971
972 function Table.update(self, data)
973         self.data = data
974 end
975
976
977
978 --[[
979 NamedSection - A fixed configuration section defined by its name
980 ]]--
981 NamedSection = class(AbstractSection)
982
983 function NamedSection.__init__(self, map, section, stype, ...)
984         AbstractSection.__init__(self, map, stype, ...)
985
986         -- Defaults
987         self.addremove = false
988         self.template = "cbi/nsection"
989         self.section = section
990 end
991
992 function NamedSection.parse(self, novld)
993         local s = self.section
994         local active = self:cfgvalue(s)
995
996         if self.addremove then
997                 local path = self.config.."."..s
998                 if active then -- Remove the section
999                         if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1000                                 self:push_events()
1001                                 return
1002                         end
1003                 else           -- Create and apply default values
1004                         if self.map:formvalue("cbi.cns."..path) then
1005                                 self:create(s)
1006                                 return
1007                         end
1008                 end
1009         end
1010
1011         if active then
1012                 AbstractSection.parse_dynamic(self, s)
1013                 if self.map:submitstate() then
1014                         Node.parse(self, s)
1015                 end
1016                 AbstractSection.parse_optionals(self, s)
1017
1018                 if self.changed then
1019                         self:push_events()
1020                 end
1021         end
1022 end
1023
1024
1025 --[[
1026 TypedSection - A (set of) configuration section(s) defined by the type
1027         addremove:      Defines whether the user can add/remove sections of this type
1028         anonymous:  Allow creating anonymous sections
1029         validate:       a validation function returning nil if the section is invalid
1030 ]]--
1031 TypedSection = class(AbstractSection)
1032
1033 function TypedSection.__init__(self, map, type, ...)
1034         AbstractSection.__init__(self, map, type, ...)
1035
1036         self.template = "cbi/tsection"
1037         self.deps = {}
1038         self.anonymous = false
1039 end
1040
1041 -- Return all matching UCI sections for this TypedSection
1042 function TypedSection.cfgsections(self)
1043         local sections = {}
1044         self.map.uci:foreach(self.map.config, self.sectiontype,
1045                 function (section)
1046                         if self:checkscope(section[".name"]) then
1047                                 table.insert(sections, section[".name"])
1048                         end
1049                 end)
1050
1051         return sections
1052 end
1053
1054 -- Limits scope to sections that have certain option => value pairs
1055 function TypedSection.depends(self, option, value)
1056         table.insert(self.deps, {option=option, value=value})
1057 end
1058
1059 function TypedSection.parse(self, novld)
1060         if self.addremove then
1061                 -- Remove
1062                 local crval = REMOVE_PREFIX .. self.config
1063                 local name = self.map:formvaluetable(crval)
1064                 for k,v in pairs(name) do
1065                         if k:sub(-2) == ".x" then
1066                                 k = k:sub(1, #k - 2)
1067                         end
1068                         if self:cfgvalue(k) and self:checkscope(k) then
1069                                 self:remove(k)
1070                         end
1071                 end
1072         end
1073
1074         local co
1075         for i, k in ipairs(self:cfgsections()) do
1076                 AbstractSection.parse_dynamic(self, k)
1077                 if self.map:submitstate() then
1078                         Node.parse(self, k, novld)
1079                 end
1080                 AbstractSection.parse_optionals(self, k)
1081         end
1082
1083         if self.addremove then
1084                 -- Create
1085                 local created
1086                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1087                 local name  = self.map:formvalue(crval)
1088                 if self.anonymous then
1089                         if name then
1090                                 created = self:create()
1091                         end
1092                 else
1093                         if name then
1094                                 -- Ignore if it already exists
1095                                 if self:cfgvalue(name) then
1096                                         name = nil;
1097                                 end
1098
1099                                 name = self:checkscope(name)
1100
1101                                 if not name then
1102                                         self.err_invalid = true
1103                                 end
1104
1105                                 if name and #name > 0 then
1106                                         created = self:create(name) and name
1107                                         if not created then
1108                                                 self.invalid_cts = true
1109                                         end
1110                                 end
1111                         end
1112                 end
1113
1114                 if created then
1115                         AbstractSection.parse_optionals(self, created)
1116                 end
1117         end
1118
1119         if created or self.changed then
1120                 self:push_events()
1121         end
1122 end
1123
1124 -- Verifies scope of sections
1125 function TypedSection.checkscope(self, section)
1126         -- Check if we are not excluded
1127         if self.filter and not self:filter(section) then
1128                 return nil
1129         end
1130
1131         -- Check if at least one dependency is met
1132         if #self.deps > 0 and self:cfgvalue(section) then
1133                 local stat = false
1134
1135                 for k, v in ipairs(self.deps) do
1136                         if self:cfgvalue(section)[v.option] == v.value then
1137                                 stat = true
1138                         end
1139                 end
1140
1141                 if not stat then
1142                         return nil
1143                 end
1144         end
1145
1146         return self:validate(section)
1147 end
1148
1149
1150 -- Dummy validate function
1151 function TypedSection.validate(self, section)
1152         return section
1153 end
1154
1155
1156 --[[
1157 AbstractValue - An abstract Value Type
1158         null:           Value can be empty
1159         valid:          A function returning the value if it is valid otherwise nil
1160         depends:        A table of option => value pairs of which one must be true
1161         default:        The default value
1162         size:           The size of the input fields
1163         rmempty:        Unset value if empty
1164         optional:       This value is optional (see AbstractSection.optionals)
1165 ]]--
1166 AbstractValue = class(Node)
1167
1168 function AbstractValue.__init__(self, map, section, option, ...)
1169         Node.__init__(self, ...)
1170         self.section = section
1171         self.option  = option
1172         self.map     = map
1173         self.config  = map.config
1174         self.tag_invalid = {}
1175         self.tag_missing = {}
1176         self.tag_reqerror = {}
1177         self.tag_error = {}
1178         self.deps = {}
1179         self.subdeps = {}
1180         --self.cast = "string"
1181
1182         self.track_missing = false
1183         self.rmempty   = true
1184         self.default   = nil
1185         self.size      = nil
1186         self.optional  = false
1187 end
1188
1189 function AbstractValue.prepare(self)
1190         self.cast = self.cast or "string"
1191 end
1192
1193 -- Add a dependencie to another section field
1194 function AbstractValue.depends(self, field, value)
1195         local deps
1196         if type(field) == "string" then
1197                 deps = {}
1198                 deps[field] = value
1199         else
1200                 deps = field
1201         end
1202
1203         table.insert(self.deps, {deps=deps, add=""})
1204 end
1205
1206 -- Generates the unique CBID
1207 function AbstractValue.cbid(self, section)
1208         return "cbid."..self.map.config.."."..section.."."..self.option
1209 end
1210
1211 -- Return whether this object should be created
1212 function AbstractValue.formcreated(self, section)
1213         local key = "cbi.opt."..self.config.."."..section
1214         return (self.map:formvalue(key) == self.option)
1215 end
1216
1217 -- Returns the formvalue for this object
1218 function AbstractValue.formvalue(self, section)
1219         return self.map:formvalue(self:cbid(section))
1220 end
1221
1222 function AbstractValue.additional(self, value)
1223         self.optional = value
1224 end
1225
1226 function AbstractValue.mandatory(self, value)
1227         self.rmempty = not value
1228 end
1229
1230 function AbstractValue.parse(self, section, novld)
1231         local fvalue = self:formvalue(section)
1232         local cvalue = self:cfgvalue(section)
1233
1234         -- If favlue and cvalue are both tables and have the same content
1235         -- make them identical
1236         if type(fvalue) == "table" and type(cvalue) == "table" then
1237                 local equal = #fvalue == #cvalue
1238                 if equal then
1239                         for i=1, #fvalue do
1240                                 if cvalue[i] ~= fvalue[i] then
1241                                         equal = false
1242                                 end
1243                         end
1244                 end
1245                 if equal then
1246                         fvalue = cvalue
1247                 end
1248         end
1249
1250         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1251                 fvalue = self:transform(self:validate(fvalue, section))
1252                 if not fvalue and not novld then
1253                         if self.error then
1254                                 self.error[section] = "invalid"
1255                         else
1256                                 self.error = { [section] = "invalid" }
1257                         end
1258                         if self.section.error then
1259                                 table.insert(self.section.error[section], "invalid")
1260                         else
1261                                 self.section.error = {[section] = {"invalid"}}
1262                         end 
1263                         self.map.save = false
1264                 end
1265                 if fvalue and not (fvalue == cvalue) then
1266                         if self:write(section, fvalue) then
1267                                 -- Push events
1268                                 self.section.changed = true
1269                                 --luci.util.append(self.map.events, self.events)
1270                         end
1271                 end
1272         else                                                    -- Unset the UCI or error
1273                 if self.rmempty or self.optional then
1274                         if self:remove(section) then
1275                                 -- Push events
1276                                 self.section.changed = true
1277                                 --luci.util.append(self.map.events, self.events)
1278                         end
1279                 elseif cvalue ~= fvalue and not novld then
1280                         self:write(section, fvalue or "")
1281                         if self.error then
1282                                 self.error[section] = "missing"
1283                         else
1284                                 self.error = { [section] = "missing" }
1285                         end
1286                         self.map.save = false
1287                 end
1288         end
1289 end
1290
1291 -- Render if this value exists or if it is mandatory
1292 function AbstractValue.render(self, s, scope)
1293         if not self.optional or next(self.section.tabs) or self:cfgvalue(s) or self:formcreated(s) then
1294                 scope = scope or {}
1295                 scope.section   = s
1296                 scope.cbid      = self:cbid(s)
1297                 scope.striptags = luci.util.striptags
1298                 scope.pcdata    = luci.util.pcdata
1299
1300                 scope.ifattr = function(cond,key,val)
1301                         if cond then
1302                                 return string.format(
1303                                         ' %s="%s"', tostring(key),
1304                                         luci.util.pcdata(tostring( val
1305                                          or scope[key]
1306                                          or (type(self[key]) ~= "function" and self[key])
1307                                          or "" ))
1308                                 )
1309                         else
1310                                 return ''
1311                         end
1312                 end
1313
1314                 scope.attr = function(...)
1315                         return scope.ifattr( true, ... )
1316                 end
1317
1318                 Node.render(self, scope)
1319         end
1320 end
1321
1322 -- Return the UCI value of this object
1323 function AbstractValue.cfgvalue(self, section)
1324         local value = (self.error and self.error[section] == "invalid")
1325                 and self:formvalue(section) or self.map:get(section, self.option)
1326         if not value then
1327                 return nil
1328         elseif not self.cast or self.cast == type(value) then
1329                 return value
1330         elseif self.cast == "string" then
1331                 if type(value) == "table" then
1332                         return value[1]
1333                 end
1334         elseif self.cast == "table" then
1335                 return luci.util.split(value, "%s+", nil, true)
1336         end
1337 end
1338
1339 -- Validate the form value
1340 function AbstractValue.validate(self, value)
1341         if self.datatype and value and datatypes[self.datatype] then
1342                 if type(value) == "table" then
1343                         local v
1344                         for _, v in ipairs(value) do
1345                                 if v and #v > 0 and not datatypes[self.datatype](v) then
1346                                         return nil
1347                                 end
1348                         end
1349                 else
1350                         if not datatypes[self.datatype](value) then
1351                                 return nil
1352                         end
1353                 end
1354         end
1355         return value
1356 end
1357
1358 AbstractValue.transform = AbstractValue.validate
1359
1360
1361 -- Write to UCI
1362 function AbstractValue.write(self, section, value)
1363         return self.map:set(section, self.option, value)
1364 end
1365
1366 -- Remove from UCI
1367 function AbstractValue.remove(self, section)
1368         return self.map:del(section, self.option)
1369 end
1370
1371
1372
1373
1374 --[[
1375 Value - A one-line value
1376         maxlength:      The maximum length
1377 ]]--
1378 Value = class(AbstractValue)
1379
1380 function Value.__init__(self, ...)
1381         AbstractValue.__init__(self, ...)
1382         self.template  = "cbi/value"
1383         self.keylist = {}
1384         self.vallist = {}
1385 end
1386
1387 function Value.value(self, key, val)
1388         val = val or key
1389         table.insert(self.keylist, tostring(key))
1390         table.insert(self.vallist, tostring(val))
1391 end
1392
1393
1394 -- DummyValue - This does nothing except being there
1395 DummyValue = class(AbstractValue)
1396
1397 function DummyValue.__init__(self, ...)
1398         AbstractValue.__init__(self, ...)
1399         self.template = "cbi/dvalue"
1400         self.value = nil
1401 end
1402
1403 function DummyValue.cfgvalue(self, section)
1404         local value
1405         if self.value then
1406                 if type(self.value) == "function" then
1407                         value = self:value(section)
1408                 else
1409                         value = self.value
1410                 end
1411         else
1412                 value = AbstractValue.cfgvalue(self, section)
1413         end
1414         return value
1415 end
1416
1417 function DummyValue.parse(self)
1418
1419 end
1420
1421
1422 --[[
1423 Flag - A flag being enabled or disabled
1424 ]]--
1425 Flag = class(AbstractValue)
1426
1427 function Flag.__init__(self, ...)
1428         AbstractValue.__init__(self, ...)
1429         self.template  = "cbi/fvalue"
1430
1431         self.enabled = "1"
1432         self.disabled = "0"
1433 end
1434
1435 -- A flag can only have two states: set or unset
1436 function Flag.parse(self, section)
1437         local fvalue = self:formvalue(section)
1438
1439         if fvalue then
1440                 fvalue = self.enabled
1441         else
1442                 fvalue = self.disabled
1443         end
1444
1445         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1446                 if not(fvalue == self:cfgvalue(section)) then
1447                         self:write(section, fvalue)
1448                 end
1449         else
1450                 self:remove(section)
1451         end
1452 end
1453
1454
1455
1456 --[[
1457 ListValue - A one-line value predefined in a list
1458         widget: The widget that will be used (select, radio)
1459 ]]--
1460 ListValue = class(AbstractValue)
1461
1462 function ListValue.__init__(self, ...)
1463         AbstractValue.__init__(self, ...)
1464         self.template  = "cbi/lvalue"
1465
1466         self.keylist = {}
1467         self.vallist = {}
1468         self.size   = 1
1469         self.widget = "select"
1470 end
1471
1472 function ListValue.value(self, key, val, ...)
1473         if luci.util.contains(self.keylist, key) then
1474                 return
1475         end
1476
1477         val = val or key
1478         table.insert(self.keylist, tostring(key))
1479         table.insert(self.vallist, tostring(val))
1480
1481         for i, deps in ipairs({...}) do
1482                 self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
1483         end
1484 end
1485
1486 function ListValue.validate(self, val)
1487         if luci.util.contains(self.keylist, val) then
1488                 return val
1489         else
1490                 return nil
1491         end
1492 end
1493
1494
1495
1496 --[[
1497 MultiValue - Multiple delimited values
1498         widget: The widget that will be used (select, checkbox)
1499         delimiter: The delimiter that will separate the values (default: " ")
1500 ]]--
1501 MultiValue = class(AbstractValue)
1502
1503 function MultiValue.__init__(self, ...)
1504         AbstractValue.__init__(self, ...)
1505         self.template = "cbi/mvalue"
1506
1507         self.keylist = {}
1508         self.vallist = {}
1509
1510         self.widget = "checkbox"
1511         self.delimiter = " "
1512 end
1513
1514 function MultiValue.render(self, ...)
1515         if self.widget == "select" and not self.size then
1516                 self.size = #self.vallist
1517         end
1518
1519         AbstractValue.render(self, ...)
1520 end
1521
1522 function MultiValue.value(self, key, val)
1523         if luci.util.contains(self.keylist, key) then
1524                 return
1525         end
1526
1527         val = val or key
1528         table.insert(self.keylist, tostring(key))
1529         table.insert(self.vallist, tostring(val))
1530 end
1531
1532 function MultiValue.valuelist(self, section)
1533         local val = self:cfgvalue(section)
1534
1535         if not(type(val) == "string") then
1536                 return {}
1537         end
1538
1539         return luci.util.split(val, self.delimiter)
1540 end
1541
1542 function MultiValue.validate(self, val)
1543         val = (type(val) == "table") and val or {val}
1544
1545         local result
1546
1547         for i, value in ipairs(val) do
1548                 if luci.util.contains(self.keylist, value) then
1549                         result = result and (result .. self.delimiter .. value) or value
1550                 end
1551         end
1552
1553         return result
1554 end
1555
1556
1557 StaticList = class(MultiValue)
1558
1559 function StaticList.__init__(self, ...)
1560         MultiValue.__init__(self, ...)
1561         self.cast = "table"
1562         self.valuelist = self.cfgvalue
1563
1564         if not self.override_scheme
1565          and self.map:get_scheme(self.section.sectiontype, self.option) then
1566                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1567                 if self.value and vs.values and not self.override_values then
1568                         for k, v in pairs(vs.values) do
1569                                 self:value(k, v)
1570                         end
1571                 end
1572         end
1573 end
1574
1575 function StaticList.validate(self, value)
1576         value = (type(value) == "table") and value or {value}
1577
1578         local valid = {}
1579         for i, v in ipairs(value) do
1580                 if luci.util.contains(self.keylist, v) then
1581                         table.insert(valid, v)
1582                 end
1583         end
1584         return valid
1585 end
1586
1587
1588 DynamicList = class(AbstractValue)
1589
1590 function DynamicList.__init__(self, ...)
1591         AbstractValue.__init__(self, ...)
1592         self.template  = "cbi/dynlist"
1593         self.cast = "table"
1594         self.keylist = {}
1595         self.vallist = {}
1596 end
1597
1598 function DynamicList.value(self, key, val)
1599         val = val or key
1600         table.insert(self.keylist, tostring(key))
1601         table.insert(self.vallist, tostring(val))
1602 end
1603
1604 function DynamicList.write(self, ...)
1605         self.map.proceed = true
1606         return AbstractValue.write(self, ...)
1607 end
1608
1609 function DynamicList.formvalue(self, section)
1610         local value = AbstractValue.formvalue(self, section)
1611         value = (type(value) == "table") and value or {value}
1612
1613         local valid = {}
1614         for i, v in ipairs(value) do
1615                 if v and #v > 0
1616                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1617                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1618                         table.insert(valid, v)
1619                 end
1620         end
1621
1622         return valid
1623 end
1624
1625
1626 --[[
1627 TextValue - A multi-line value
1628         rows:   Rows
1629 ]]--
1630 TextValue = class(AbstractValue)
1631
1632 function TextValue.__init__(self, ...)
1633         AbstractValue.__init__(self, ...)
1634         self.template  = "cbi/tvalue"
1635 end
1636
1637 --[[
1638 Button
1639 ]]--
1640 Button = class(AbstractValue)
1641
1642 function Button.__init__(self, ...)
1643         AbstractValue.__init__(self, ...)
1644         self.template  = "cbi/button"
1645         self.inputstyle = nil
1646         self.rmempty = true
1647 end
1648
1649
1650 FileUpload = class(AbstractValue)
1651
1652 function FileUpload.__init__(self, ...)
1653         AbstractValue.__init__(self, ...)
1654         self.template = "cbi/upload"
1655         if not self.map.upload_fields then
1656                 self.map.upload_fields = { self }
1657         else
1658                 self.map.upload_fields[#self.map.upload_fields+1] = self
1659         end
1660 end
1661
1662 function FileUpload.formcreated(self, section)
1663         return AbstractValue.formcreated(self, section) or
1664                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1665                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1666 end
1667
1668 function FileUpload.cfgvalue(self, section)
1669         local val = AbstractValue.cfgvalue(self, section)
1670         if val and fs.access(val) then
1671                 return val
1672         end
1673         return nil
1674 end
1675
1676 function FileUpload.formvalue(self, section)
1677         local val = AbstractValue.formvalue(self, section)
1678         if val then
1679                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1680                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1681                 then
1682                         return val
1683                 end
1684                 fs.unlink(val)
1685                 self.value = nil
1686         end
1687         return nil
1688 end
1689
1690 function FileUpload.remove(self, section)
1691         local val = AbstractValue.formvalue(self, section)
1692         if val and fs.access(val) then fs.unlink(val) end
1693         return AbstractValue.remove(self, section)
1694 end
1695
1696
1697 FileBrowser = class(AbstractValue)
1698
1699 function FileBrowser.__init__(self, ...)
1700         AbstractValue.__init__(self, ...)
1701         self.template = "cbi/browser"
1702 end