5fa992dee8e740a88efde18d3163440553dcb2bb
[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                 self:_run_hooks("on_save", "on_before_save")
308                 for i, config in ipairs(self.parsechain) do
309                         self.uci:save(config)
310                 end
311                 self:_run_hooks("on_after_save")
312                 if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
313                         self:_run_hooks("on_before_commit")
314                         for i, config in ipairs(self.parsechain) do
315                                 self.uci:commit(config)
316
317                                 -- Refresh data because commit changes section names
318                                 self.uci:load(config)
319                         end
320                         self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
321                         if self.apply_on_parse then
322                                 self.uci:apply(self.parsechain)
323                                 self:_run_hooks("on_apply", "on_after_apply")
324                         else
325                                 self._apply = function()
326                                         local cmd = self.uci:apply(self.parsechain, true)
327                                         return io.popen(cmd)
328                                 end
329                         end
330
331                         -- Reparse sections
332                         Node.parse(self, true)
333
334                 end
335                 for i, config in ipairs(self.parsechain) do
336                         self.uci:unload(config)
337                 end
338                 if type(self.commit_handler) == "function" then
339                         self:commit_handler(self:submitstate())
340                 end
341         end
342
343         if self:submitstate() then
344                 if not self.save then
345                         self.state = FORM_INVALID
346                 elseif self.proceed then
347                         self.state = FORM_PROCEED
348                 else
349                         self.state = self.changed and FORM_CHANGED or FORM_VALID
350                 end
351         else
352                 self.state = FORM_NODATA
353         end
354
355         return self:state_handler(self.state)
356 end
357
358 function Map.render(self, ...)
359         self:_run_hooks("on_init")
360         Node.render(self, ...)
361         if false and self._apply then
362                 local fp = self._apply()
363                 fp:read("*a")
364                 fp:close()
365                 self:_run_hooks("on_apply")
366         end
367 end
368
369 -- Creates a child section
370 function Map.section(self, class, ...)
371         if instanceof(class, AbstractSection) then
372                 local obj  = class(self, ...)
373                 self:append(obj)
374                 return obj
375         else
376                 error("class must be a descendent of AbstractSection")
377         end
378 end
379
380 -- UCI add
381 function Map.add(self, sectiontype)
382         return self.uci:add(self.config, sectiontype)
383 end
384
385 -- UCI set
386 function Map.set(self, section, option, value)
387         if option then
388                 return self.uci:set(self.config, section, option, value)
389         else
390                 return self.uci:set(self.config, section, value)
391         end
392 end
393
394 -- UCI del
395 function Map.del(self, section, option)
396         if option then
397                 return self.uci:delete(self.config, section, option)
398         else
399                 return self.uci:delete(self.config, section)
400         end
401 end
402
403 -- UCI get
404 function Map.get(self, section, option)
405         if not section then
406                 return self.uci:get_all(self.config)
407         elseif option then
408                 return self.uci:get(self.config, section, option)
409         else
410                 return self.uci:get_all(self.config, section)
411         end
412 end
413
414 --[[
415 Compound - Container
416 ]]--
417 Compound = class(Node)
418
419 function Compound.__init__(self, ...)
420         Node.__init__(self)
421         self.template = "cbi/compound"
422         self.children = {...}
423 end
424
425 function Compound.populate_delegator(self, delegator)
426         for _, v in ipairs(self.children) do
427                 v.delegator = delegator
428         end
429 end
430
431 function Compound.parse(self, ...)
432         local cstate, state = 0
433
434         for k, child in ipairs(self.children) do
435                 cstate = child:parse(...)
436                 state = (not state or cstate < state) and cstate or state
437         end
438
439         return state
440 end
441
442
443 --[[
444 Delegator - Node controller
445 ]]--
446 Delegator = class(Node)
447 function Delegator.__init__(self, ...)
448         Node.__init__(self, ...)
449         self.nodes = {}
450         self.defaultpath = {}
451         self.pageaction = false
452         self.readinput = true
453         self.allow_reset = false
454         self.allow_cancel = false
455         self.allow_back = false
456         self.allow_finish = false
457         self.template = "cbi/delegator"
458 end
459
460 function Delegator.set(self, name, node)
461         assert(not self.nodes[name], "Duplicate entry")
462
463         self.nodes[name] = node
464 end
465
466 function Delegator.add(self, name, node)
467         node = self:set(name, node)
468         self.defaultpath[#self.defaultpath+1] = name
469 end
470
471 function Delegator.insert_after(self, name, after)
472         local n = #self.chain + 1
473         for k, v in ipairs(self.chain) do
474                 if v == after then
475                         n = k + 1
476                         break
477                 end
478         end
479         table.insert(self.chain, n, name)
480 end
481
482 function Delegator.set_route(self, ...)
483         local n, chain, route = 0, self.chain, {...}
484         for i = 1, #chain do
485                 if chain[i] == self.current then
486                         n = i
487                         break
488                 end
489         end
490         for i = 1, #route do
491                 n = n + 1
492                 chain[n] = route[i]
493         end
494         for i = n + 1, #chain do
495                 chain[i] = nil
496         end
497 end
498
499 function Delegator.get(self, name)
500         local node = self.nodes[name]
501
502         if type(node) == "string" then
503                 node = load(node, name)
504         end
505
506         if type(node) == "table" and getmetatable(node) == nil then
507                 node = Compound(unpack(node))
508         end
509
510         return node
511 end
512
513 function Delegator.parse(self, ...)
514         if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
515                 if self:_run_hooks("on_cancel") then
516                         return FORM_DONE
517                 end
518         end
519
520         if not Map.formvalue(self, "cbi.delg.current") then
521                 self:_run_hooks("on_init")
522         end
523
524         local newcurrent
525         self.chain = self.chain or self:get_chain()
526         self.current = self.current or self:get_active()
527         self.active = self.active or self:get(self.current)
528         assert(self.active, "Invalid state")
529
530         local stat = FORM_DONE
531         if type(self.active) ~= "function" then
532                 self.active:populate_delegator(self)
533                 stat = self.active:parse()
534         else
535                 self:active()
536         end
537
538         if stat > FORM_PROCEED then
539                 if Map.formvalue(self, "cbi.delg.back") then
540                         newcurrent = self:get_prev(self.current)
541                 else
542                         newcurrent = self:get_next(self.current)
543                 end
544         elseif stat < FORM_PROCEED then
545                 return stat
546         end
547
548
549         if not Map.formvalue(self, "cbi.submit") then
550                 return FORM_NODATA
551         elseif stat > FORM_PROCEED
552         and (not newcurrent or not self:get(newcurrent)) then
553                 return self:_run_hook("on_done") or FORM_DONE
554         else
555                 self.current = newcurrent or self.current
556                 self.active = self:get(self.current)
557                 if type(self.active) ~= "function" then
558                         self.active:populate_delegator(self)
559                         local stat = self.active:parse(false)
560                         if stat == FORM_SKIP then
561                                 return self:parse(...)
562                         else
563                                 return FORM_PROCEED
564                         end
565                 else
566                         return self:parse(...)
567                 end
568         end
569 end
570
571 function Delegator.get_next(self, state)
572         for k, v in ipairs(self.chain) do
573                 if v == state then
574                         return self.chain[k+1]
575                 end
576         end
577 end
578
579 function Delegator.get_prev(self, state)
580         for k, v in ipairs(self.chain) do
581                 if v == state then
582                         return self.chain[k-1]
583                 end
584         end
585 end
586
587 function Delegator.get_chain(self)
588         local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
589         return type(x) == "table" and x or {x}
590 end
591
592 function Delegator.get_active(self)
593         return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
594 end
595
596 --[[
597 Page - A simple node
598 ]]--
599
600 Page = class(Node)
601 Page.__init__ = Node.__init__
602 Page.parse    = function() end
603
604
605 --[[
606 SimpleForm - A Simple non-UCI form
607 ]]--
608 SimpleForm = class(Node)
609
610 function SimpleForm.__init__(self, config, title, description, data)
611         Node.__init__(self, title, description)
612         self.config = config
613         self.data = data or {}
614         self.template = "cbi/simpleform"
615         self.dorender = true
616         self.pageaction = false
617         self.readinput = true
618 end
619
620 SimpleForm.formvalue = Map.formvalue
621 SimpleForm.formvaluetable = Map.formvaluetable
622
623 function SimpleForm.parse(self, readinput, ...)
624         self.readinput = (readinput ~= false)
625
626         if self:formvalue("cbi.skip") then
627                 return FORM_SKIP
628         end
629
630         if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
631                 return FORM_DONE
632         end
633
634         if self:submitstate() then
635                 Node.parse(self, 1, ...)
636         end
637
638         local valid = true
639         for k, j in ipairs(self.children) do
640                 for i, v in ipairs(j.children) do
641                         valid = valid
642                          and (not v.tag_missing or not v.tag_missing[1])
643                          and (not v.tag_invalid or not v.tag_invalid[1])
644                          and (not v.error)
645                 end
646         end
647
648         local state =
649                 not self:submitstate() and FORM_NODATA
650                 or valid and FORM_VALID
651                 or FORM_INVALID
652
653         self.dorender = not self.handle
654         if self.handle then
655                 local nrender, nstate = self:handle(state, self.data)
656                 self.dorender = self.dorender or (nrender ~= false)
657                 state = nstate or state
658         end
659         return state
660 end
661
662 function SimpleForm.render(self, ...)
663         if self.dorender then
664                 Node.render(self, ...)
665         end
666 end
667
668 function SimpleForm.submitstate(self)
669         return self:formvalue("cbi.submit")
670 end
671
672 function SimpleForm.section(self, class, ...)
673         if instanceof(class, AbstractSection) then
674                 local obj  = class(self, ...)
675                 self:append(obj)
676                 return obj
677         else
678                 error("class must be a descendent of AbstractSection")
679         end
680 end
681
682 -- Creates a child field
683 function SimpleForm.field(self, class, ...)
684         local section
685         for k, v in ipairs(self.children) do
686                 if instanceof(v, SimpleSection) then
687                         section = v
688                         break
689                 end
690         end
691         if not section then
692                 section = self:section(SimpleSection)
693         end
694
695         if instanceof(class, AbstractValue) then
696                 local obj  = class(self, section, ...)
697                 obj.track_missing = true
698                 section:append(obj)
699                 return obj
700         else
701                 error("class must be a descendent of AbstractValue")
702         end
703 end
704
705 function SimpleForm.set(self, section, option, value)
706         self.data[option] = value
707 end
708
709
710 function SimpleForm.del(self, section, option)
711         self.data[option] = nil
712 end
713
714
715 function SimpleForm.get(self, section, option)
716         return self.data[option]
717 end
718
719
720 function SimpleForm.get_scheme()
721         return nil
722 end
723
724
725 Form = class(SimpleForm)
726
727 function Form.__init__(self, ...)
728         SimpleForm.__init__(self, ...)
729         self.embedded = true
730 end
731
732
733 --[[
734 AbstractSection
735 ]]--
736 AbstractSection = class(Node)
737
738 function AbstractSection.__init__(self, map, sectiontype, ...)
739         Node.__init__(self, ...)
740         self.sectiontype = sectiontype
741         self.map = map
742         self.config = map.config
743         self.optionals = {}
744         self.defaults = {}
745         self.fields = {}
746         self.tag_error = {}
747         self.tag_invalid = {}
748         self.tag_deperror = {}
749         self.changed = false
750
751         self.optional = true
752         self.addremove = false
753         self.dynamic = false
754 end
755
756 -- Define a tab for the section
757 function AbstractSection.tab(self, tab, title, desc)
758         self.tabs      = self.tabs      or { }
759         self.tab_names = self.tab_names or { }
760
761         self.tab_names[#self.tab_names+1] = tab
762         self.tabs[tab] = {
763                 title       = title,
764                 description = desc,
765                 childs      = { }
766         }
767 end
768
769 -- Check whether the section has tabs
770 function AbstractSection.has_tabs(self)
771         return (self.tabs ~= nil) and (next(self.tabs) ~= nil)
772 end
773
774 -- Appends a new option
775 function AbstractSection.option(self, class, option, ...)
776         if instanceof(class, AbstractValue) then
777                 local obj  = class(self.map, self, option, ...)
778                 self:append(obj)
779                 self.fields[option] = obj
780                 return obj
781         elseif class == true then
782                 error("No valid class was given and autodetection failed.")
783         else
784                 error("class must be a descendant of AbstractValue")
785         end
786 end
787
788 -- Appends a new tabbed option
789 function AbstractSection.taboption(self, tab, ...)
790
791         assert(tab and self.tabs and self.tabs[tab],
792                 "Cannot assign option to not existing tab %q" % tostring(tab))
793
794         local l = self.tabs[tab].childs
795         local o = AbstractSection.option(self, ...)
796
797         if o then l[#l+1] = o end
798
799         return o
800 end
801
802 -- Render a single tab
803 function AbstractSection.render_tab(self, tab, ...)
804
805         assert(tab and self.tabs and self.tabs[tab],
806                 "Cannot render not existing tab %q" % tostring(tab))
807
808         for _, node in ipairs(self.tabs[tab].childs) do
809                 node:render(...)
810         end
811 end
812
813 -- Parse optional options
814 function AbstractSection.parse_optionals(self, section)
815         if not self.optional then
816                 return
817         end
818
819         self.optionals[section] = {}
820
821         local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
822         for k,v in ipairs(self.children) do
823                 if v.optional and not v:cfgvalue(section) and not self:has_tabs() then
824                         if field == v.option then
825                                 field = nil
826                                 self.map.proceed = true
827                         else
828                                 table.insert(self.optionals[section], v)
829                         end
830                 end
831         end
832
833         if field and #field > 0 and self.dynamic then
834                 self:add_dynamic(field)
835         end
836 end
837
838 -- Add a dynamic option
839 function AbstractSection.add_dynamic(self, field, optional)
840         local o = self:option(Value, field, field)
841         o.optional = optional
842 end
843
844 -- Parse all dynamic options
845 function AbstractSection.parse_dynamic(self, section)
846         if not self.dynamic then
847                 return
848         end
849
850         local arr  = luci.util.clone(self:cfgvalue(section))
851         local form = self.map:formvaluetable("cbid."..self.config.."."..section)
852         for k, v in pairs(form) do
853                 arr[k] = v
854         end
855
856         for key,val in pairs(arr) do
857                 local create = true
858
859                 for i,c in ipairs(self.children) do
860                         if c.option == key then
861                                 create = false
862                         end
863                 end
864
865                 if create and key:sub(1, 1) ~= "." then
866                         self.map.proceed = true
867                         self:add_dynamic(key, true)
868                 end
869         end
870 end
871
872 -- Returns the section's UCI table
873 function AbstractSection.cfgvalue(self, section)
874         return self.map:get(section)
875 end
876
877 -- Push events
878 function AbstractSection.push_events(self)
879         --luci.util.append(self.map.events, self.events)
880         self.map.changed = true
881 end
882
883 -- Removes the section
884 function AbstractSection.remove(self, section)
885         self.map.proceed = true
886         return self.map:del(section)
887 end
888
889 -- Creates the section
890 function AbstractSection.create(self, section)
891         local stat
892
893         if section then
894                 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
895         else
896                 section = self.map:add(self.sectiontype)
897                 stat = section
898         end
899
900         if stat then
901                 for k,v in pairs(self.children) do
902                         if v.default then
903                                 self.map:set(section, v.option, v.default)
904                         end
905                 end
906
907                 for k,v in pairs(self.defaults) do
908                         self.map:set(section, k, v)
909                 end
910         end
911
912         self.map.proceed = true
913
914         return stat
915 end
916
917
918 SimpleSection = class(AbstractSection)
919
920 function SimpleSection.__init__(self, form, ...)
921         AbstractSection.__init__(self, form, nil, ...)
922         self.template = "cbi/nullsection"
923 end
924
925
926 Table = class(AbstractSection)
927
928 function Table.__init__(self, form, data, ...)
929         local datasource = {}
930         local tself = self
931         datasource.config = "table"
932         self.data = data or {}
933
934         datasource.formvalue = Map.formvalue
935         datasource.formvaluetable = Map.formvaluetable
936         datasource.readinput = true
937
938         function datasource.get(self, section, option)
939                 return tself.data[section] and tself.data[section][option]
940         end
941
942         function datasource.submitstate(self)
943                 return Map.formvalue(self, "cbi.submit")
944         end
945
946         function datasource.del(...)
947                 return true
948         end
949
950         function datasource.get_scheme()
951                 return nil
952         end
953
954         AbstractSection.__init__(self, datasource, "table", ...)
955         self.template = "cbi/tblsection"
956         self.rowcolors = true
957         self.anonymous = true
958 end
959
960 function Table.parse(self, readinput)
961         self.map.readinput = (readinput ~= false)
962         for i, k in ipairs(self:cfgsections()) do
963                 if self.map:submitstate() then
964                         Node.parse(self, k)
965                 end
966         end
967 end
968
969 function Table.cfgsections(self)
970         local sections = {}
971
972         for i, v in luci.util.kspairs(self.data) do
973                 table.insert(sections, i)
974         end
975
976         return sections
977 end
978
979 function Table.update(self, data)
980         self.data = data
981 end
982
983
984
985 --[[
986 NamedSection - A fixed configuration section defined by its name
987 ]]--
988 NamedSection = class(AbstractSection)
989
990 function NamedSection.__init__(self, map, section, stype, ...)
991         AbstractSection.__init__(self, map, stype, ...)
992
993         -- Defaults
994         self.addremove = false
995         self.template = "cbi/nsection"
996         self.section = section
997 end
998
999 function NamedSection.parse(self, novld)
1000         local s = self.section
1001         local active = self:cfgvalue(s)
1002
1003         if self.addremove then
1004                 local path = self.config.."."..s
1005                 if active then -- Remove the section
1006                         if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1007                                 self:push_events()
1008                                 return
1009                         end
1010                 else           -- Create and apply default values
1011                         if self.map:formvalue("cbi.cns."..path) then
1012                                 self:create(s)
1013                                 return
1014                         end
1015                 end
1016         end
1017
1018         if active then
1019                 AbstractSection.parse_dynamic(self, s)
1020                 if self.map:submitstate() then
1021                         Node.parse(self, s)
1022                 end
1023                 AbstractSection.parse_optionals(self, s)
1024
1025                 if self.changed then
1026                         self:push_events()
1027                 end
1028         end
1029 end
1030
1031
1032 --[[
1033 TypedSection - A (set of) configuration section(s) defined by the type
1034         addremove:      Defines whether the user can add/remove sections of this type
1035         anonymous:  Allow creating anonymous sections
1036         validate:       a validation function returning nil if the section is invalid
1037 ]]--
1038 TypedSection = class(AbstractSection)
1039
1040 function TypedSection.__init__(self, map, type, ...)
1041         AbstractSection.__init__(self, map, type, ...)
1042
1043         self.template = "cbi/tsection"
1044         self.deps = {}
1045         self.anonymous = false
1046 end
1047
1048 -- Return all matching UCI sections for this TypedSection
1049 function TypedSection.cfgsections(self)
1050         local sections = {}
1051         self.map.uci:foreach(self.map.config, self.sectiontype,
1052                 function (section)
1053                         if self:checkscope(section[".name"]) then
1054                                 table.insert(sections, section[".name"])
1055                         end
1056                 end)
1057
1058         return sections
1059 end
1060
1061 -- Limits scope to sections that have certain option => value pairs
1062 function TypedSection.depends(self, option, value)
1063         table.insert(self.deps, {option=option, value=value})
1064 end
1065
1066 function TypedSection.parse(self, novld)
1067         if self.addremove then
1068                 -- Remove
1069                 local crval = REMOVE_PREFIX .. self.config
1070                 local name = self.map:formvaluetable(crval)
1071                 for k,v in pairs(name) do
1072                         if k:sub(-2) == ".x" then
1073                                 k = k:sub(1, #k - 2)
1074                         end
1075                         if self:cfgvalue(k) and self:checkscope(k) then
1076                                 self:remove(k)
1077                         end
1078                 end
1079         end
1080
1081         local co
1082         for i, k in ipairs(self:cfgsections()) do
1083                 AbstractSection.parse_dynamic(self, k)
1084                 if self.map:submitstate() then
1085                         Node.parse(self, k, novld)
1086                 end
1087                 AbstractSection.parse_optionals(self, k)
1088         end
1089
1090         if self.addremove then
1091                 -- Create
1092                 local created
1093                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1094                 local name  = self.map:formvalue(crval)
1095                 if self.anonymous then
1096                         if name then
1097                                 created = self:create()
1098                         end
1099                 else
1100                         if name then
1101                                 -- Ignore if it already exists
1102                                 if self:cfgvalue(name) then
1103                                         name = nil;
1104                                 end
1105
1106                                 name = self:checkscope(name)
1107
1108                                 if not name then
1109                                         self.err_invalid = true
1110                                 end
1111
1112                                 if name and #name > 0 then
1113                                         created = self:create(name) and name
1114                                         if not created then
1115                                                 self.invalid_cts = true
1116                                         end
1117                                 end
1118                         end
1119                 end
1120
1121                 if created then
1122                         AbstractSection.parse_optionals(self, created)
1123                 end
1124         end
1125
1126         if created or self.changed then
1127                 self:push_events()
1128         end
1129 end
1130
1131 -- Verifies scope of sections
1132 function TypedSection.checkscope(self, section)
1133         -- Check if we are not excluded
1134         if self.filter and not self:filter(section) then
1135                 return nil
1136         end
1137
1138         -- Check if at least one dependency is met
1139         if #self.deps > 0 and self:cfgvalue(section) then
1140                 local stat = false
1141
1142                 for k, v in ipairs(self.deps) do
1143                         if self:cfgvalue(section)[v.option] == v.value then
1144                                 stat = true
1145                         end
1146                 end
1147
1148                 if not stat then
1149                         return nil
1150                 end
1151         end
1152
1153         return self:validate(section)
1154 end
1155
1156
1157 -- Dummy validate function
1158 function TypedSection.validate(self, section)
1159         return section
1160 end
1161
1162
1163 --[[
1164 AbstractValue - An abstract Value Type
1165         null:           Value can be empty
1166         valid:          A function returning the value if it is valid otherwise nil
1167         depends:        A table of option => value pairs of which one must be true
1168         default:        The default value
1169         size:           The size of the input fields
1170         rmempty:        Unset value if empty
1171         optional:       This value is optional (see AbstractSection.optionals)
1172 ]]--
1173 AbstractValue = class(Node)
1174
1175 function AbstractValue.__init__(self, map, section, option, ...)
1176         Node.__init__(self, ...)
1177         self.section = section
1178         self.option  = option
1179         self.map     = map
1180         self.config  = map.config
1181         self.tag_invalid = {}
1182         self.tag_missing = {}
1183         self.tag_reqerror = {}
1184         self.tag_error = {}
1185         self.deps = {}
1186         self.subdeps = {}
1187         --self.cast = "string"
1188
1189         self.track_missing = false
1190         self.rmempty   = true
1191         self.default   = nil
1192         self.size      = nil
1193         self.optional  = false
1194 end
1195
1196 function AbstractValue.prepare(self)
1197         self.cast = self.cast or "string"
1198 end
1199
1200 -- Add a dependencie to another section field
1201 function AbstractValue.depends(self, field, value)
1202         local deps
1203         if type(field) == "string" then
1204                 deps = {}
1205                 deps[field] = value
1206         else
1207                 deps = field
1208         end
1209
1210         table.insert(self.deps, {deps=deps, add=""})
1211 end
1212
1213 -- Generates the unique CBID
1214 function AbstractValue.cbid(self, section)
1215         return "cbid."..self.map.config.."."..section.."."..self.option
1216 end
1217
1218 -- Return whether this object should be created
1219 function AbstractValue.formcreated(self, section)
1220         local key = "cbi.opt."..self.config.."."..section
1221         return (self.map:formvalue(key) == self.option)
1222 end
1223
1224 -- Returns the formvalue for this object
1225 function AbstractValue.formvalue(self, section)
1226         return self.map:formvalue(self:cbid(section))
1227 end
1228
1229 function AbstractValue.additional(self, value)
1230         self.optional = value
1231 end
1232
1233 function AbstractValue.mandatory(self, value)
1234         self.rmempty = not value
1235 end
1236
1237 function AbstractValue.add_error(self, section, type, msg)
1238         self.error = self.error or { }
1239         self.error[section] = msg or type
1240
1241         self.section.error = self.section.error or { }
1242         self.section.error[section] = self.section.error[section] or { }
1243         table.insert(self.section.error[section], msg or type)
1244
1245         if type == "invalid" then
1246                 self.tag_invalid[section] = true
1247         elseif type == "missing" then
1248                 self.tag_missing[section] = true
1249         end
1250
1251         self.tag_error[section] = true
1252         self.map.save = false
1253 end
1254
1255 function AbstractValue.parse(self, section, novld)
1256         local fvalue = self:formvalue(section)
1257         local cvalue = self:cfgvalue(section)
1258
1259         -- If favlue and cvalue are both tables and have the same content
1260         -- make them identical
1261         if type(fvalue) == "table" and type(cvalue) == "table" then
1262                 local equal = #fvalue == #cvalue
1263                 if equal then
1264                         for i=1, #fvalue do
1265                                 if cvalue[i] ~= fvalue[i] then
1266                                         equal = false
1267                                 end
1268                         end
1269                 end
1270                 if equal then
1271                         fvalue = cvalue
1272                 end
1273         end
1274
1275         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1276                 local val_err
1277                 fvalue, val_err = self:validate(fvalue, section)
1278                 fvalue = self:transform(fvalue)
1279
1280                 if not fvalue and not novld then
1281                         self:add_error(section, "invalid", val_err)
1282                 end
1283
1284                 if fvalue and not (fvalue == cvalue) then
1285                         if self:write(section, fvalue) then
1286                                 -- Push events
1287                                 self.section.changed = true
1288                                 --luci.util.append(self.map.events, self.events)
1289                         end
1290                 end
1291         else                                                    -- Unset the UCI or error
1292                 if self.rmempty or self.optional then
1293                         if self:remove(section) then
1294                                 -- Push events
1295                                 self.section.changed = true
1296                                 --luci.util.append(self.map.events, self.events)
1297                         end
1298                 elseif cvalue ~= fvalue and not novld then
1299                         -- trigger validator with nil value to get custom user error msg.
1300                         local _, val_err = self:validate(nil, section)
1301                         self:add_error(section, "missing", val_err)
1302                 end
1303         end
1304 end
1305
1306 -- Render if this value exists or if it is mandatory
1307 function AbstractValue.render(self, s, scope)
1308         if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
1309                 scope = scope or {}
1310                 scope.section   = s
1311                 scope.cbid      = self:cbid(s)
1312                 scope.striptags = luci.util.striptags
1313                 scope.pcdata    = luci.util.pcdata
1314
1315                 scope.ifattr = function(cond,key,val)
1316                         if cond then
1317                                 return string.format(
1318                                         ' %s="%s"', tostring(key),
1319                                         luci.util.pcdata(tostring( val
1320                                          or scope[key]
1321                                          or (type(self[key]) ~= "function" and self[key])
1322                                          or "" ))
1323                                 )
1324                         else
1325                                 return ''
1326                         end
1327                 end
1328
1329                 scope.attr = function(...)
1330                         return scope.ifattr( true, ... )
1331                 end
1332
1333                 Node.render(self, scope)
1334         end
1335 end
1336
1337 -- Return the UCI value of this object
1338 function AbstractValue.cfgvalue(self, section)
1339         local value
1340         if self.tag_error[section] then
1341                 value = self:formvalue(section)
1342         else
1343                 value = self.map:get(section, self.option)
1344         end
1345
1346         if not value then
1347                 return nil
1348         elseif not self.cast or self.cast == type(value) then
1349                 return value
1350         elseif self.cast == "string" then
1351                 if type(value) == "table" then
1352                         return value[1]
1353                 end
1354         elseif self.cast == "table" then
1355                 return luci.util.split(value, "%s+", nil, true)
1356         end
1357 end
1358
1359 -- Validate the form value
1360 function AbstractValue.validate(self, value)
1361         if self.datatype and value and datatypes[self.datatype] then
1362                 if type(value) == "table" then
1363                         local v
1364                         for _, v in ipairs(value) do
1365                                 if v and #v > 0 and not datatypes[self.datatype](v) then
1366                                         return nil
1367                                 end
1368                         end
1369                 else
1370                         if not datatypes[self.datatype](value) then
1371                                 return nil
1372                         end
1373                 end
1374         end
1375         return value
1376 end
1377
1378 AbstractValue.transform = AbstractValue.validate
1379
1380
1381 -- Write to UCI
1382 function AbstractValue.write(self, section, value)
1383         return self.map:set(section, self.option, value)
1384 end
1385
1386 -- Remove from UCI
1387 function AbstractValue.remove(self, section)
1388         return self.map:del(section, self.option)
1389 end
1390
1391
1392
1393
1394 --[[
1395 Value - A one-line value
1396         maxlength:      The maximum length
1397 ]]--
1398 Value = class(AbstractValue)
1399
1400 function Value.__init__(self, ...)
1401         AbstractValue.__init__(self, ...)
1402         self.template  = "cbi/value"
1403         self.keylist = {}
1404         self.vallist = {}
1405 end
1406
1407 function Value.reset_values(self)
1408         self.keylist = {}
1409         self.vallist = {}
1410 end
1411
1412 function Value.value(self, key, val)
1413         val = val or key
1414         table.insert(self.keylist, tostring(key))
1415         table.insert(self.vallist, tostring(val))
1416 end
1417
1418
1419 -- DummyValue - This does nothing except being there
1420 DummyValue = class(AbstractValue)
1421
1422 function DummyValue.__init__(self, ...)
1423         AbstractValue.__init__(self, ...)
1424         self.template = "cbi/dvalue"
1425         self.value = nil
1426 end
1427
1428 function DummyValue.cfgvalue(self, section)
1429         local value
1430         if self.value then
1431                 if type(self.value) == "function" then
1432                         value = self:value(section)
1433                 else
1434                         value = self.value
1435                 end
1436         else
1437                 value = AbstractValue.cfgvalue(self, section)
1438         end
1439         return value
1440 end
1441
1442 function DummyValue.parse(self)
1443
1444 end
1445
1446
1447 --[[
1448 Flag - A flag being enabled or disabled
1449 ]]--
1450 Flag = class(AbstractValue)
1451
1452 function Flag.__init__(self, ...)
1453         AbstractValue.__init__(self, ...)
1454         self.template  = "cbi/fvalue"
1455
1456         self.enabled = "1"
1457         self.disabled = "0"
1458 end
1459
1460 -- A flag can only have two states: set or unset
1461 function Flag.parse(self, section)
1462         local fvalue = self:formvalue(section)
1463
1464         if fvalue then
1465                 fvalue = self.enabled
1466         else
1467                 fvalue = self.disabled
1468         end
1469
1470         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1471                 if not(fvalue == self:cfgvalue(section)) then
1472                         self:write(section, fvalue)
1473                 end
1474         else
1475                 self:remove(section)
1476         end
1477 end
1478
1479
1480
1481 --[[
1482 ListValue - A one-line value predefined in a list
1483         widget: The widget that will be used (select, radio)
1484 ]]--
1485 ListValue = class(AbstractValue)
1486
1487 function ListValue.__init__(self, ...)
1488         AbstractValue.__init__(self, ...)
1489         self.template  = "cbi/lvalue"
1490
1491         self.keylist = {}
1492         self.vallist = {}
1493         self.size   = 1
1494         self.widget = "select"
1495 end
1496
1497 function ListValue.reset_values(self)
1498         self.keylist = {}
1499         self.vallist = {}
1500 end
1501
1502 function ListValue.value(self, key, val, ...)
1503         if luci.util.contains(self.keylist, key) then
1504                 return
1505         end
1506
1507         val = val or key
1508         table.insert(self.keylist, tostring(key))
1509         table.insert(self.vallist, tostring(val))
1510
1511         for i, deps in ipairs({...}) do
1512                 self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
1513         end
1514 end
1515
1516 function ListValue.validate(self, val)
1517         if luci.util.contains(self.keylist, val) then
1518                 return val
1519         else
1520                 return nil
1521         end
1522 end
1523
1524
1525
1526 --[[
1527 MultiValue - Multiple delimited values
1528         widget: The widget that will be used (select, checkbox)
1529         delimiter: The delimiter that will separate the values (default: " ")
1530 ]]--
1531 MultiValue = class(AbstractValue)
1532
1533 function MultiValue.__init__(self, ...)
1534         AbstractValue.__init__(self, ...)
1535         self.template = "cbi/mvalue"
1536
1537         self.keylist = {}
1538         self.vallist = {}
1539
1540         self.widget = "checkbox"
1541         self.delimiter = " "
1542 end
1543
1544 function MultiValue.render(self, ...)
1545         if self.widget == "select" and not self.size then
1546                 self.size = #self.vallist
1547         end
1548
1549         AbstractValue.render(self, ...)
1550 end
1551
1552 function MultiValue.reset_values(self)
1553         self.keylist = {}
1554         self.vallist = {}
1555 end
1556
1557 function MultiValue.value(self, key, val)
1558         if luci.util.contains(self.keylist, key) then
1559                 return
1560         end
1561
1562         val = val or key
1563         table.insert(self.keylist, tostring(key))
1564         table.insert(self.vallist, tostring(val))
1565 end
1566
1567 function MultiValue.valuelist(self, section)
1568         local val = self:cfgvalue(section)
1569
1570         if not(type(val) == "string") then
1571                 return {}
1572         end
1573
1574         return luci.util.split(val, self.delimiter)
1575 end
1576
1577 function MultiValue.validate(self, val)
1578         val = (type(val) == "table") and val or {val}
1579
1580         local result
1581
1582         for i, value in ipairs(val) do
1583                 if luci.util.contains(self.keylist, value) then
1584                         result = result and (result .. self.delimiter .. value) or value
1585                 end
1586         end
1587
1588         return result
1589 end
1590
1591
1592 StaticList = class(MultiValue)
1593
1594 function StaticList.__init__(self, ...)
1595         MultiValue.__init__(self, ...)
1596         self.cast = "table"
1597         self.valuelist = self.cfgvalue
1598
1599         if not self.override_scheme
1600          and self.map:get_scheme(self.section.sectiontype, self.option) then
1601                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1602                 if self.value and vs.values and not self.override_values then
1603                         for k, v in pairs(vs.values) do
1604                                 self:value(k, v)
1605                         end
1606                 end
1607         end
1608 end
1609
1610 function StaticList.validate(self, value)
1611         value = (type(value) == "table") and value or {value}
1612
1613         local valid = {}
1614         for i, v in ipairs(value) do
1615                 if luci.util.contains(self.keylist, v) then
1616                         table.insert(valid, v)
1617                 end
1618         end
1619         return valid
1620 end
1621
1622
1623 DynamicList = class(AbstractValue)
1624
1625 function DynamicList.__init__(self, ...)
1626         AbstractValue.__init__(self, ...)
1627         self.template  = "cbi/dynlist"
1628         self.cast = "table"
1629         self.keylist = {}
1630         self.vallist = {}
1631 end
1632
1633 function DynamicList.reset_values(self)
1634         self.keylist = {}
1635         self.vallist = {}
1636 end
1637
1638 function DynamicList.value(self, key, val)
1639         val = val or key
1640         table.insert(self.keylist, tostring(key))
1641         table.insert(self.vallist, tostring(val))
1642 end
1643
1644 function DynamicList.write(self, ...)
1645         self.map.proceed = true
1646         return AbstractValue.write(self, ...)
1647 end
1648
1649 function DynamicList.formvalue(self, section)
1650         local value = AbstractValue.formvalue(self, section)
1651         value = (type(value) == "table") and value or {value}
1652
1653         local valid = {}
1654         for i, v in ipairs(value) do
1655                 if v and #v > 0
1656                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1657                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1658                         table.insert(valid, v)
1659                 end
1660         end
1661
1662         return valid
1663 end
1664
1665
1666 --[[
1667 TextValue - A multi-line value
1668         rows:   Rows
1669 ]]--
1670 TextValue = class(AbstractValue)
1671
1672 function TextValue.__init__(self, ...)
1673         AbstractValue.__init__(self, ...)
1674         self.template  = "cbi/tvalue"
1675 end
1676
1677 --[[
1678 Button
1679 ]]--
1680 Button = class(AbstractValue)
1681
1682 function Button.__init__(self, ...)
1683         AbstractValue.__init__(self, ...)
1684         self.template  = "cbi/button"
1685         self.inputstyle = nil
1686         self.rmempty = true
1687 end
1688
1689
1690 FileUpload = class(AbstractValue)
1691
1692 function FileUpload.__init__(self, ...)
1693         AbstractValue.__init__(self, ...)
1694         self.template = "cbi/upload"
1695         if not self.map.upload_fields then
1696                 self.map.upload_fields = { self }
1697         else
1698                 self.map.upload_fields[#self.map.upload_fields+1] = self
1699         end
1700 end
1701
1702 function FileUpload.formcreated(self, section)
1703         return AbstractValue.formcreated(self, section) or
1704                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1705                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1706 end
1707
1708 function FileUpload.cfgvalue(self, section)
1709         local val = AbstractValue.cfgvalue(self, section)
1710         if val and fs.access(val) then
1711                 return val
1712         end
1713         return nil
1714 end
1715
1716 function FileUpload.formvalue(self, section)
1717         local val = AbstractValue.formvalue(self, section)
1718         if val then
1719                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1720                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1721                 then
1722                         return val
1723                 end
1724                 fs.unlink(val)
1725                 self.value = nil
1726         end
1727         return nil
1728 end
1729
1730 function FileUpload.remove(self, section)
1731         local val = AbstractValue.formvalue(self, section)
1732         if val and fs.access(val) then fs.unlink(val) end
1733         return AbstractValue.remove(self, section)
1734 end
1735
1736
1737 FileBrowser = class(AbstractValue)
1738
1739 function FileBrowser.__init__(self, ...)
1740         AbstractValue.__init__(self, ...)
1741         self.template = "cbi/browser"
1742 end