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