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