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