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