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