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