993b422f660131bbc43aef105575df514417dd3c
[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.parse(self, section, novld)
1236         local fvalue = self:formvalue(section)
1237         local cvalue = self:cfgvalue(section)
1238
1239         -- If favlue and cvalue are both tables and have the same content
1240         -- make them identical
1241         if type(fvalue) == "table" and type(cvalue) == "table" then
1242                 local equal = #fvalue == #cvalue
1243                 if equal then
1244                         for i=1, #fvalue do
1245                                 if cvalue[i] ~= fvalue[i] then
1246                                         equal = false
1247                                 end
1248                         end
1249                 end
1250                 if equal then
1251                         fvalue = cvalue
1252                 end
1253         end
1254
1255         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1256                 local val_err
1257                 fvalue, val_err = self:validate(fvalue, section)
1258                 fvalue = self:transform(fvalue)
1259
1260                 if not fvalue and not novld then
1261                         val_err = val_err or "invalid"
1262
1263                         self.error = self.error or { }
1264                         self.error[section] = val_err
1265
1266                         self.section.error = self.section.error or { }
1267                         self.section.error[section] = self.section.error[section] or { }
1268                         table.insert(self.section.error[section], val_err)
1269
1270                         self.map.save = false
1271                 end
1272                 if fvalue and not (fvalue == cvalue) then
1273                         if self:write(section, fvalue) then
1274                                 -- Push events
1275                                 self.section.changed = true
1276                                 --luci.util.append(self.map.events, self.events)
1277                         end
1278                 end
1279         else                                                    -- Unset the UCI or error
1280                 if self.rmempty or self.optional then
1281                         if self:remove(section) then
1282                                 -- Push events
1283                                 self.section.changed = true
1284                                 --luci.util.append(self.map.events, self.events)
1285                         end
1286                 elseif cvalue ~= fvalue and not novld then
1287                         self:write(section, fvalue or "")
1288                         if self.error then
1289                                 self.error[section] = "missing"
1290                         else
1291                                 self.error = { [section] = "missing" }
1292                         end
1293                         self.map.save = false
1294                 end
1295         end
1296 end
1297
1298 -- Render if this value exists or if it is mandatory
1299 function AbstractValue.render(self, s, scope)
1300         if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
1301                 scope = scope or {}
1302                 scope.section   = s
1303                 scope.cbid      = self:cbid(s)
1304                 scope.striptags = luci.util.striptags
1305                 scope.pcdata    = luci.util.pcdata
1306
1307                 scope.ifattr = function(cond,key,val)
1308                         if cond then
1309                                 return string.format(
1310                                         ' %s="%s"', tostring(key),
1311                                         luci.util.pcdata(tostring( val
1312                                          or scope[key]
1313                                          or (type(self[key]) ~= "function" and self[key])
1314                                          or "" ))
1315                                 )
1316                         else
1317                                 return ''
1318                         end
1319                 end
1320
1321                 scope.attr = function(...)
1322                         return scope.ifattr( true, ... )
1323                 end
1324
1325                 Node.render(self, scope)
1326         end
1327 end
1328
1329 -- Return the UCI value of this object
1330 function AbstractValue.cfgvalue(self, section)
1331         local value = (self.error and self.error[section] == "invalid")
1332                 and self:formvalue(section) or self.map:get(section, self.option)
1333         if not value then
1334                 return nil
1335         elseif not self.cast or self.cast == type(value) then
1336                 return value
1337         elseif self.cast == "string" then
1338                 if type(value) == "table" then
1339                         return value[1]
1340                 end
1341         elseif self.cast == "table" then
1342                 return luci.util.split(value, "%s+", nil, true)
1343         end
1344 end
1345
1346 -- Validate the form value
1347 function AbstractValue.validate(self, value)
1348         if self.datatype and value and datatypes[self.datatype] then
1349                 if type(value) == "table" then
1350                         local v
1351                         for _, v in ipairs(value) do
1352                                 if v and #v > 0 and not datatypes[self.datatype](v) then
1353                                         return nil
1354                                 end
1355                         end
1356                 else
1357                         if not datatypes[self.datatype](value) then
1358                                 return nil
1359                         end
1360                 end
1361         end
1362         return value
1363 end
1364
1365 AbstractValue.transform = AbstractValue.validate
1366
1367
1368 -- Write to UCI
1369 function AbstractValue.write(self, section, value)
1370         return self.map:set(section, self.option, value)
1371 end
1372
1373 -- Remove from UCI
1374 function AbstractValue.remove(self, section)
1375         return self.map:del(section, self.option)
1376 end
1377
1378
1379
1380
1381 --[[
1382 Value - A one-line value
1383         maxlength:      The maximum length
1384 ]]--
1385 Value = class(AbstractValue)
1386
1387 function Value.__init__(self, ...)
1388         AbstractValue.__init__(self, ...)
1389         self.template  = "cbi/value"
1390         self.keylist = {}
1391         self.vallist = {}
1392 end
1393
1394 function Value.value(self, key, val)
1395         val = val or key
1396         table.insert(self.keylist, tostring(key))
1397         table.insert(self.vallist, tostring(val))
1398 end
1399
1400
1401 -- DummyValue - This does nothing except being there
1402 DummyValue = class(AbstractValue)
1403
1404 function DummyValue.__init__(self, ...)
1405         AbstractValue.__init__(self, ...)
1406         self.template = "cbi/dvalue"
1407         self.value = nil
1408 end
1409
1410 function DummyValue.cfgvalue(self, section)
1411         local value
1412         if self.value then
1413                 if type(self.value) == "function" then
1414                         value = self:value(section)
1415                 else
1416                         value = self.value
1417                 end
1418         else
1419                 value = AbstractValue.cfgvalue(self, section)
1420         end
1421         return value
1422 end
1423
1424 function DummyValue.parse(self)
1425
1426 end
1427
1428
1429 --[[
1430 Flag - A flag being enabled or disabled
1431 ]]--
1432 Flag = class(AbstractValue)
1433
1434 function Flag.__init__(self, ...)
1435         AbstractValue.__init__(self, ...)
1436         self.template  = "cbi/fvalue"
1437
1438         self.enabled = "1"
1439         self.disabled = "0"
1440 end
1441
1442 -- A flag can only have two states: set or unset
1443 function Flag.parse(self, section)
1444         local fvalue = self:formvalue(section)
1445
1446         if fvalue then
1447                 fvalue = self.enabled
1448         else
1449                 fvalue = self.disabled
1450         end
1451
1452         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1453                 if not(fvalue == self:cfgvalue(section)) then
1454                         self:write(section, fvalue)
1455                 end
1456         else
1457                 self:remove(section)
1458         end
1459 end
1460
1461
1462
1463 --[[
1464 ListValue - A one-line value predefined in a list
1465         widget: The widget that will be used (select, radio)
1466 ]]--
1467 ListValue = class(AbstractValue)
1468
1469 function ListValue.__init__(self, ...)
1470         AbstractValue.__init__(self, ...)
1471         self.template  = "cbi/lvalue"
1472
1473         self.keylist = {}
1474         self.vallist = {}
1475         self.size   = 1
1476         self.widget = "select"
1477 end
1478
1479 function ListValue.value(self, key, val, ...)
1480         if luci.util.contains(self.keylist, key) then
1481                 return
1482         end
1483
1484         val = val or key
1485         table.insert(self.keylist, tostring(key))
1486         table.insert(self.vallist, tostring(val))
1487
1488         for i, deps in ipairs({...}) do
1489                 self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
1490         end
1491 end
1492
1493 function ListValue.validate(self, val)
1494         if luci.util.contains(self.keylist, val) then
1495                 return val
1496         else
1497                 return nil
1498         end
1499 end
1500
1501
1502
1503 --[[
1504 MultiValue - Multiple delimited values
1505         widget: The widget that will be used (select, checkbox)
1506         delimiter: The delimiter that will separate the values (default: " ")
1507 ]]--
1508 MultiValue = class(AbstractValue)
1509
1510 function MultiValue.__init__(self, ...)
1511         AbstractValue.__init__(self, ...)
1512         self.template = "cbi/mvalue"
1513
1514         self.keylist = {}
1515         self.vallist = {}
1516
1517         self.widget = "checkbox"
1518         self.delimiter = " "
1519 end
1520
1521 function MultiValue.render(self, ...)
1522         if self.widget == "select" and not self.size then
1523                 self.size = #self.vallist
1524         end
1525
1526         AbstractValue.render(self, ...)
1527 end
1528
1529 function MultiValue.value(self, key, val)
1530         if luci.util.contains(self.keylist, key) then
1531                 return
1532         end
1533
1534         val = val or key
1535         table.insert(self.keylist, tostring(key))
1536         table.insert(self.vallist, tostring(val))
1537 end
1538
1539 function MultiValue.valuelist(self, section)
1540         local val = self:cfgvalue(section)
1541
1542         if not(type(val) == "string") then
1543                 return {}
1544         end
1545
1546         return luci.util.split(val, self.delimiter)
1547 end
1548
1549 function MultiValue.validate(self, val)
1550         val = (type(val) == "table") and val or {val}
1551
1552         local result
1553
1554         for i, value in ipairs(val) do
1555                 if luci.util.contains(self.keylist, value) then
1556                         result = result and (result .. self.delimiter .. value) or value
1557                 end
1558         end
1559
1560         return result
1561 end
1562
1563
1564 StaticList = class(MultiValue)
1565
1566 function StaticList.__init__(self, ...)
1567         MultiValue.__init__(self, ...)
1568         self.cast = "table"
1569         self.valuelist = self.cfgvalue
1570
1571         if not self.override_scheme
1572          and self.map:get_scheme(self.section.sectiontype, self.option) then
1573                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1574                 if self.value and vs.values and not self.override_values then
1575                         for k, v in pairs(vs.values) do
1576                                 self:value(k, v)
1577                         end
1578                 end
1579         end
1580 end
1581
1582 function StaticList.validate(self, value)
1583         value = (type(value) == "table") and value or {value}
1584
1585         local valid = {}
1586         for i, v in ipairs(value) do
1587                 if luci.util.contains(self.keylist, v) then
1588                         table.insert(valid, v)
1589                 end
1590         end
1591         return valid
1592 end
1593
1594
1595 DynamicList = class(AbstractValue)
1596
1597 function DynamicList.__init__(self, ...)
1598         AbstractValue.__init__(self, ...)
1599         self.template  = "cbi/dynlist"
1600         self.cast = "table"
1601         self.keylist = {}
1602         self.vallist = {}
1603 end
1604
1605 function DynamicList.value(self, key, val)
1606         val = val or key
1607         table.insert(self.keylist, tostring(key))
1608         table.insert(self.vallist, tostring(val))
1609 end
1610
1611 function DynamicList.write(self, ...)
1612         self.map.proceed = true
1613         return AbstractValue.write(self, ...)
1614 end
1615
1616 function DynamicList.formvalue(self, section)
1617         local value = AbstractValue.formvalue(self, section)
1618         value = (type(value) == "table") and value or {value}
1619
1620         local valid = {}
1621         for i, v in ipairs(value) do
1622                 if v and #v > 0
1623                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1624                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1625                         table.insert(valid, v)
1626                 end
1627         end
1628
1629         return valid
1630 end
1631
1632
1633 --[[
1634 TextValue - A multi-line value
1635         rows:   Rows
1636 ]]--
1637 TextValue = class(AbstractValue)
1638
1639 function TextValue.__init__(self, ...)
1640         AbstractValue.__init__(self, ...)
1641         self.template  = "cbi/tvalue"
1642 end
1643
1644 --[[
1645 Button
1646 ]]--
1647 Button = class(AbstractValue)
1648
1649 function Button.__init__(self, ...)
1650         AbstractValue.__init__(self, ...)
1651         self.template  = "cbi/button"
1652         self.inputstyle = nil
1653         self.rmempty = true
1654 end
1655
1656
1657 FileUpload = class(AbstractValue)
1658
1659 function FileUpload.__init__(self, ...)
1660         AbstractValue.__init__(self, ...)
1661         self.template = "cbi/upload"
1662         if not self.map.upload_fields then
1663                 self.map.upload_fields = { self }
1664         else
1665                 self.map.upload_fields[#self.map.upload_fields+1] = self
1666         end
1667 end
1668
1669 function FileUpload.formcreated(self, section)
1670         return AbstractValue.formcreated(self, section) or
1671                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1672                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1673 end
1674
1675 function FileUpload.cfgvalue(self, section)
1676         local val = AbstractValue.cfgvalue(self, section)
1677         if val and fs.access(val) then
1678                 return val
1679         end
1680         return nil
1681 end
1682
1683 function FileUpload.formvalue(self, section)
1684         local val = AbstractValue.formvalue(self, section)
1685         if val then
1686                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1687                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1688                 then
1689                         return val
1690                 end
1691                 fs.unlink(val)
1692                 self.value = nil
1693         end
1694         return nil
1695 end
1696
1697 function FileUpload.remove(self, section)
1698         local val = AbstractValue.formvalue(self, section)
1699         if val and fs.access(val) then fs.unlink(val) end
1700         return AbstractValue.remove(self, section)
1701 end
1702
1703
1704 FileBrowser = class(AbstractValue)
1705
1706 function FileBrowser.__init__(self, ...)
1707         AbstractValue.__init__(self, ...)
1708         self.template = "cbi/browser"
1709 end