CBI-UVL integration part #1
[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 require("luci.util")
31 require("luci.http")
32 require("luci.model.uci")
33 require("luci.uvl")
34
35 local uci        = luci.model.uci
36 local class      = luci.util.class
37 local instanceof = luci.util.instanceof
38
39 FORM_NODATA  =  0
40 FORM_VALID   =  1
41 FORM_INVALID = -1
42
43 CREATE_PREFIX = "cbi.cts."
44 REMOVE_PREFIX = "cbi.rts."
45
46 -- Loads a CBI map from given file, creating an environment and returns it
47 function load(cbimap, ...)
48         require("luci.fs")
49         require("luci.i18n")
50         require("luci.config")
51         require("luci.util")
52
53         local cbidir = luci.util.libpath() .. "/model/cbi/"
54         local func, err = loadfile(cbidir..cbimap..".lua")
55
56         if not func then
57                 return nil
58         end
59
60         luci.i18n.loadc("cbi")
61
62         luci.util.resfenv(func)
63         luci.util.updfenv(func, luci.cbi)
64         luci.util.extfenv(func, "translate", luci.i18n.translate)
65         luci.util.extfenv(func, "translatef", luci.i18n.translatef)
66         luci.util.extfenv(func, "arg", {...})
67
68         local maps = {func()}
69
70         for i, map in ipairs(maps) do
71                 if not instanceof(map, Node) then
72                         error("CBI map returns no valid map object!")
73                         return nil
74                 end
75         end
76
77         return maps
78 end
79
80 -- Node pseudo abstract class
81 Node = class()
82
83 function Node.__init__(self, title, description)
84         self.children = {}
85         self.title = title or ""
86         self.description = description or ""
87         self.template = "cbi/node"
88 end
89
90 -- i18n helper
91 function Node._i18n(self, config, section, option, title, description)
92
93         -- i18n loaded?
94         if type(luci.i18n) == "table" then
95
96                 local key = config and config:gsub("[^%w]+", "") or ""
97
98                 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
99                 if option  then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "")  end
100
101                 self.title = title or luci.i18n.translate( key, option or section or config )
102                 self.description = description or luci.i18n.translate( key .. "_desc", "" )
103         end
104 end
105
106 -- Append child nodes
107 function Node.append(self, obj)
108         table.insert(self.children, obj)
109 end
110
111 -- Parse this node and its children
112 function Node.parse(self, ...)
113         for k, child in ipairs(self.children) do
114                 child:parse(...)
115         end
116 end
117
118 -- Render this node
119 function Node.render(self, scope)
120         scope = scope or {}
121         scope.self = self
122
123         luci.template.render(self.template, scope)
124 end
125
126 -- Render the children
127 function Node.render_children(self, ...)
128         for k, node in ipairs(self.children) do
129                 node:render(...)
130         end
131 end
132
133
134 --[[
135 A simple template element
136 ]]--
137 Template = class(Node)
138
139 function Template.__init__(self, template)
140         Node.__init__(self)
141         self.template = template
142 end
143
144 function Template.render(self)
145         luci.template.render(self.template, {self=self})
146 end
147
148
149 --[[
150 Map - A map describing a configuration file
151 ]]--
152 Map = class(Node)
153
154 function Map.__init__(self, config, ...)
155         Node.__init__(self, ...)
156         Node._i18n(self, config, nil, nil, ...)
157
158         self.config = config
159         self.parsechain = {self.config}
160         self.template = "cbi/map"
161         if not uci.load_config(self.config) then
162                 error("Unable to read UCI data: " .. self.config)
163         end
164
165         self.validator = luci.uvl.UVL()
166         self.scheme = self.validator:get_scheme(self.config)
167 end
168
169 function Map.render(self, ...)
170         if self.stateful then
171                 uci.load_state(self.config)
172         else
173                 uci.load_config(self.config)
174         end
175         Node.render(self, ...)
176 end
177
178
179 -- Chain foreign config
180 function Map.chain(self, config)
181         table.insert(self.parsechain, config)
182 end
183
184 -- Use optimized UCI writing
185 function Map.parse(self, ...)
186         if self.stateful then
187                 uci.load_state(self.config)
188         else
189                 uci.load_config(self.config)
190         end
191         
192         Node.parse(self, ...)
193         
194         for i, config in ipairs(self.parsechain) do
195                 uci.save_config(config)
196         end
197         if luci.http.formvalue("cbi.apply") then
198                 for i, config in ipairs(self.parsechain) do
199                         uci.commit(config)
200                         if luci.config.uci_oncommit and luci.config.uci_oncommit[config] then
201                                 luci.util.exec(luci.config.uci_oncommit[config])
202                         end
203
204                         -- Refresh data because commit changes section names
205                         uci.load_config(config)
206                 end
207
208                 -- Reparse sections
209                 Node.parse(self, ...)
210
211         end
212         for i, config in ipairs(self.parsechain) do
213                 uci.unload(config)
214         end
215 end
216
217 -- Creates a child section
218 function Map.section(self, class, ...)
219         if instanceof(class, AbstractSection) then
220                 local obj  = class(self, ...)
221                 self:append(obj)
222                 return obj
223         else
224                 error("class must be a descendent of AbstractSection")
225         end
226 end
227
228 -- UCI add
229 function Map.add(self, sectiontype)
230         return uci.add(self.config, sectiontype)
231 end
232
233 -- UCI set
234 function Map.set(self, section, option, value)
235         if option then
236                 return uci.set(self.config, section, option, value)
237         else
238                 return uci.set(self.config, section, value)
239         end
240 end
241
242 -- UCI del
243 function Map.del(self, section, option)
244         if option then
245                 return uci.delete(self.config, section, option)
246         else
247                 return uci.delete(self.config, section)
248         end
249 end
250
251 -- UCI get
252 function Map.get(self, section, option)
253         if not section then
254                 return uci.get_all(self.config)
255         elseif option then
256                 return uci.get(self.config, section, option)
257         else
258                 return uci.get_all(self.config, section)
259         end
260 end
261
262
263 --[[
264 Page - A simple node
265 ]]--
266
267 Page = class(Node)
268 Page.__init__ = Node.__init__
269 Page.parse    = function() end
270
271
272 --[[
273 SimpleForm - A Simple non-UCI form
274 ]]--
275 SimpleForm = class(Node)
276
277 function SimpleForm.__init__(self, config, title, description, data)
278         Node.__init__(self, title, description)
279         self.config = config
280         self.data = data or {}
281         self.template = "cbi/simpleform"
282         self.dorender = true
283 end
284
285 function SimpleForm.parse(self, ...)
286         if luci.http.formvalue("cbi.submit") then
287                 Node.parse(self, 1, ...)
288         end
289                 
290         local valid = true
291         for k, j in ipairs(self.children) do 
292                 for i, v in ipairs(j.children) do
293                         valid = valid 
294                          and (not v.tag_missing or not v.tag_missing[1])
295                          and (not v.tag_invalid or not v.tag_invalid[1])
296                 end
297         end
298         
299         local state = 
300                 not luci.http.formvalue("cbi.submit") and 0
301                 or valid and 1
302                 or -1
303
304         self.dorender = not self.handle or self:handle(state, self.data) ~= false
305 end
306
307 function SimpleForm.render(self, ...)
308         if self.dorender then
309                 Node.render(self, ...)
310         end
311 end
312
313 function SimpleForm.section(self, class, ...)
314         if instanceof(class, AbstractSection) then
315                 local obj  = class(self, ...)
316                 self:append(obj)
317                 return obj
318         else
319                 error("class must be a descendent of AbstractSection")
320         end
321 end
322
323 -- Creates a child field
324 function SimpleForm.field(self, class, ...)
325         local section
326         for k, v in ipairs(self.children) do
327                 if instanceof(v, SimpleSection) then
328                         section = v
329                         break
330                 end
331         end
332         if not section then
333                 section = self:section(SimpleSection)
334         end
335         
336         if instanceof(class, AbstractValue) then
337                 local obj  = class(self, ...)
338                 obj.track_missing = true
339                 section:append(obj)
340                 return obj
341         else
342                 error("class must be a descendent of AbstractValue")
343         end
344 end
345
346 function SimpleForm.set(self, section, option, value)
347         self.data[option] = value
348 end
349
350
351 function SimpleForm.del(self, section, option)
352         self.data[option] = nil
353 end
354
355
356 function SimpleForm.get(self, section, option)
357         return self.data[option]
358 end
359
360
361
362 --[[
363 AbstractSection
364 ]]--
365 AbstractSection = class(Node)
366
367 function AbstractSection.__init__(self, map, sectiontype, ...)
368         Node.__init__(self, ...)
369         self.sectiontype = sectiontype
370         self.map = map
371         self.config = map.config
372         self.optionals = {}
373         self.defaults = {}
374
375         self.optional = true
376         self.addremove = false
377         self.dynamic = false
378 end
379
380 -- Appends a new option
381 function AbstractSection.option(self, class, option, ...)
382         if instanceof(class, AbstractValue) then
383                 local obj  = class(self.map, option, ...)
384
385                 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
386
387                 self:append(obj)
388                 return obj
389         else
390                 error("class must be a descendent of AbstractValue")
391         end
392 end
393
394 -- Parse optional options
395 function AbstractSection.parse_optionals(self, section)
396         if not self.optional then
397                 return
398         end
399
400         self.optionals[section] = {}
401
402         local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
403         for k,v in ipairs(self.children) do
404                 if v.optional and not v:cfgvalue(section) then
405                         if field == v.option then
406                                 field = nil
407                         else
408                                 table.insert(self.optionals[section], v)
409                         end
410                 end
411         end
412
413         if field and #field > 0 and self.dynamic then
414                 self:add_dynamic(field)
415         end
416 end
417
418 -- Add a dynamic option
419 function AbstractSection.add_dynamic(self, field, optional)
420         local o = self:option(Value, field, field)
421         o.optional = optional
422 end
423
424 -- Parse all dynamic options
425 function AbstractSection.parse_dynamic(self, section)
426         if not self.dynamic then
427                 return
428         end
429
430         local arr  = luci.util.clone(self:cfgvalue(section))
431         local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
432         for k, v in pairs(form) do
433                 arr[k] = v
434         end
435
436         for key,val in pairs(arr) do
437                 local create = true
438
439                 for i,c in ipairs(self.children) do
440                         if c.option == key then
441                                 create = false
442                         end
443                 end
444
445                 if create and key:sub(1, 1) ~= "." then
446                         self:add_dynamic(key, true)
447                 end
448         end
449 end
450
451 -- Returns the section's UCI table
452 function AbstractSection.cfgvalue(self, section)
453         return self.map:get(section)
454 end
455
456 -- Removes the section
457 function AbstractSection.remove(self, section)
458         return self.map:del(section)
459 end
460
461 -- Creates the section
462 function AbstractSection.create(self, section)
463         local stat
464         
465         if section then
466                 stat = self.map:set(section, nil, self.sectiontype)
467         else
468                 section = self.map:add(self.sectiontype)
469                 stat = section
470         end
471
472         if stat then
473                 for k,v in pairs(self.children) do
474                         if v.default then
475                                 self.map:set(section, v.option, v.default)
476                         end
477                 end
478
479                 for k,v in pairs(self.defaults) do
480                         self.map:set(section, k, v)
481                 end
482         end
483
484         return stat
485 end
486
487
488 SimpleSection = class(AbstractSection)
489
490 function SimpleSection.__init__(self, form, ...)
491         AbstractSection.__init__(self, form, nil, ...)
492         self.template = "cbi/nullsection"
493 end
494
495
496 Table = class(AbstractSection)
497
498 function Table.__init__(self, form, data, ...)
499         local datasource = {}
500         datasource.config = "table"
501         self.data = data
502         
503         function datasource.get(self, section, option)
504                 return data[section] and data[section][option]
505         end
506         
507         function datasource.del(...)
508                 return true
509         end
510         
511         AbstractSection.__init__(self, datasource, "table", ...)
512         self.template = "cbi/tblsection"
513         self.rowcolors = true
514         self.anonymous = true
515 end
516
517 function Table.parse(self)
518         for i, k in ipairs(self:cfgsections()) do
519                 if luci.http.formvalue("cbi.submit") then
520                         Node.parse(self, k)
521                 end
522         end
523 end
524
525 function Table.cfgsections(self)
526         local sections = {}
527         
528         for i, v in luci.util.kspairs(self.data) do
529                 table.insert(sections, i)
530         end
531         
532         return sections
533 end
534
535
536
537 --[[
538 NamedSection - A fixed configuration section defined by its name
539 ]]--
540 NamedSection = class(AbstractSection)
541
542 function NamedSection.__init__(self, map, section, stype, ...)
543         AbstractSection.__init__(self, map, stype, ...)
544         Node._i18n(self, map.config, section, nil, ...)
545
546         -- Defaults
547         self.addremove = false
548
549         -- Use defaults from UVL
550         if self.map.scheme and self.map.scheme.sections[self.sectiontype] then
551                 local vs = self.map.scheme.sections[self.sectiontype]
552                 self.addremove = not vs.unique and not vs.required
553                 self.dynamic   = vs.dynamic
554         end
555
556         self.template = "cbi/nsection"
557         self.section = section
558 end
559
560 function NamedSection.parse(self)
561         local s = self.section
562         local active = self:cfgvalue(s)
563
564
565         if self.addremove then
566                 local path = self.config.."."..s
567                 if active then -- Remove the section
568                         if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
569                                 return
570                         end
571                 else           -- Create and apply default values
572                         if luci.http.formvalue("cbi.cns."..path) then
573                                 self:create(s)
574                                 return
575                         end
576                 end
577         end
578
579         if active then
580                 AbstractSection.parse_dynamic(self, s)
581                 if luci.http.formvalue("cbi.submit") then
582                         Node.parse(self, s)
583                 end
584                 AbstractSection.parse_optionals(self, s)
585         end
586 end
587
588
589 --[[
590 TypedSection - A (set of) configuration section(s) defined by the type
591         addremove:      Defines whether the user can add/remove sections of this type
592         anonymous:  Allow creating anonymous sections
593         validate:       a validation function returning nil if the section is invalid
594 ]]--
595 TypedSection = class(AbstractSection)
596
597 function TypedSection.__init__(self, map, type, ...)
598         AbstractSection.__init__(self, map, type, ...)
599         Node._i18n(self, map.config, type, nil, ...)
600
601         self.template  = "cbi/tsection"
602         self.deps = {}
603         self.anonymous = false
604
605         -- Use defaults from UVL
606         if self.map.scheme and self.map.scheme.sections[self.sectiontype] then
607                 local vs = self.map.scheme.sections[self.sectiontype]
608                 self.addremove = not vs.unique and not vs.required
609                 self.dynamic   = vs.dynamic
610                 self.anonymous = not vs.named
611         end
612 end
613
614 -- Return all matching UCI sections for this TypedSection
615 function TypedSection.cfgsections(self)
616         local sections = {}
617         uci.foreach(self.map.config, self.sectiontype,
618                 function (section)
619                         if self:checkscope(section[".name"]) then
620                                 table.insert(sections, section[".name"])
621                         end
622                 end)
623
624         return sections
625 end
626
627 -- Limits scope to sections that have certain option => value pairs
628 function TypedSection.depends(self, option, value)
629         table.insert(self.deps, {option=option, value=value})
630 end
631
632 function TypedSection.parse(self)
633         if self.addremove then
634                 -- Create
635                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
636                 local name  = luci.http.formvalue(crval)
637                 if self.anonymous then
638                         if name then
639                                 self:create()
640                         end
641                 else
642                         if name then
643                                 -- Ignore if it already exists
644                                 if self:cfgvalue(name) then
645                                         name = nil;
646                                 end
647
648                                 name = self:checkscope(name)
649
650                                 if not name then
651                                         self.err_invalid = true
652                                 end
653
654                                 if name and name:len() > 0 then
655                                         self:create(name)
656                                 end
657                         end
658                 end
659
660                 -- Remove
661                 crval = REMOVE_PREFIX .. self.config
662                 name = luci.http.formvaluetable(crval)
663                 for k,v in pairs(name) do
664                         luci.util.perror(k)
665                         luci.util.perror(self:cfgvalue(k))
666                         luci.util.perror(self:checkscope(k))
667                         if self:cfgvalue(k) and self:checkscope(k) then
668                                 self:remove(k)
669                         end
670                 end
671         end
672
673         for i, k in ipairs(self:cfgsections()) do
674                 AbstractSection.parse_dynamic(self, k)
675                 if luci.http.formvalue("cbi.submit") then
676                         Node.parse(self, k)
677                 end
678                 AbstractSection.parse_optionals(self, k)
679         end
680 end
681
682 -- Verifies scope of sections
683 function TypedSection.checkscope(self, section)
684         -- Check if we are not excluded
685         if self.filter and not self:filter(section) then
686                 return nil
687         end
688
689         -- Check if at least one dependency is met
690         if #self.deps > 0 and self:cfgvalue(section) then
691                 local stat = false
692
693                 for k, v in ipairs(self.deps) do
694                         if self:cfgvalue(section)[v.option] == v.value then
695                                 stat = true
696                         end
697                 end
698
699                 if not stat then
700                         return nil
701                 end
702         end
703
704         return self:validate(section)
705 end
706
707
708 -- Dummy validate function
709 function TypedSection.validate(self, section)
710         return section
711 end
712
713
714 --[[
715 AbstractValue - An abstract Value Type
716         null:           Value can be empty
717         valid:          A function returning the value if it is valid otherwise nil
718         depends:        A table of option => value pairs of which one must be true
719         default:        The default value
720         size:           The size of the input fields
721         rmempty:        Unset value if empty
722         optional:       This value is optional (see AbstractSection.optionals)
723 ]]--
724 AbstractValue = class(Node)
725
726 function AbstractValue.__init__(self, map, option, ...)
727         Node.__init__(self, ...)
728         self.option = option
729         self.map    = map
730         self.config = map.config
731         self.tag_invalid = {}
732         self.tag_missing = {}
733         self.tag_error = {}
734         self.deps = {}
735         self.cast = "string"
736
737         self.track_missing = false
738         self.rmempty   = false
739         self.default   = nil
740         self.size      = nil
741         self.optional  = false
742 end
743
744 -- Add a dependencie to another section field
745 function AbstractValue.depends(self, field, value)
746         local deps
747         if type(field) == "string" then
748                 deps = {}
749                 deps[field] = value
750         else
751                 deps = field
752         end
753         
754         table.insert(self.deps, {deps=deps, add=""})
755 end
756
757 -- Generates the unique CBID
758 function AbstractValue.cbid(self, section)
759         return "cbid."..self.map.config.."."..section.."."..self.option
760 end
761
762 -- Return whether this object should be created
763 function AbstractValue.formcreated(self, section)
764         local key = "cbi.opt."..self.config.."."..section
765         return (luci.http.formvalue(key) == self.option)
766 end
767
768 -- Returns the formvalue for this object
769 function AbstractValue.formvalue(self, section)
770         return luci.http.formvalue(self:cbid(section))
771 end
772
773 function AbstractValue.additional(self, value)
774         self.optional = value
775 end
776
777 function AbstractValue.mandatory(self, value)
778         self.rmempty = not value
779 end
780
781 function AbstractValue.parse(self, section)
782         local fvalue = self:formvalue(section)
783         local cvalue = self:cfgvalue(section)
784
785         if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
786                 fvalue = self:transform(self:validate(fvalue, section))
787                 if not fvalue then
788                         self.tag_invalid[section] = true
789                 end
790                 if fvalue and not (fvalue == cvalue) then
791                         self:write(section, fvalue)
792                 end
793         else                                                    -- Unset the UCI or error
794                 if self.rmempty or self.optional then
795                         self:remove(section)
796                 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
797                         self.tag_missing[section] = true
798                 end
799         end
800 end
801
802 -- Render if this value exists or if it is mandatory
803 function AbstractValue.render(self, s, scope)
804         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
805                 scope = scope or {}
806                 scope.section   = s
807                 scope.cbid      = self:cbid(s)
808                 scope.striptags = luci.util.striptags
809
810                 scope.ifattr = function(cond,key,val)
811                         if cond then
812                                 return string.format(
813                                         ' %s="%s"', tostring(key),
814                                         luci.util.pcdata(tostring( val
815                                          or scope[key]
816                                          or (type(self[key]) ~= "function" and self[key])
817                                          or "" ))
818                                 )
819                         else
820                                 return ''
821                         end
822                 end
823
824                 scope.attr = function(...)
825                         return scope.ifattr( true, ... )
826                 end
827
828                 Node.render(self, scope)
829         end
830 end
831
832 -- Return the UCI value of this object
833 function AbstractValue.cfgvalue(self, section)
834         local value = self.map:get(section, self.option)
835         if not self.cast or self.cast == type(value) then
836                 return value
837         elseif self.cast == "string" then
838                 if type(value) == "table" then
839                         return value[1]
840                 end
841         elseif self.cast == "table" then
842                 return {value}
843         end
844 end
845
846 -- Validate the form value
847 function AbstractValue.validate(self, value)
848         return value
849 end
850
851 AbstractValue.transform = AbstractValue.validate
852
853
854 -- Write to UCI
855 function AbstractValue.write(self, section, value)
856         return self.map:set(section, self.option, value)
857 end
858
859 -- Remove from UCI
860 function AbstractValue.remove(self, section)
861         return self.map:del(section, self.option)
862 end
863
864
865
866
867 --[[
868 Value - A one-line value
869         maxlength:      The maximum length
870 ]]--
871 Value = class(AbstractValue)
872
873 function Value.__init__(self, ...)
874         AbstractValue.__init__(self, ...)
875         self.template  = "cbi/value"
876         self.keylist = {}
877         self.vallist = {}
878 end
879
880 function Value.value(self, key, val)
881         val = val or key
882         table.insert(self.keylist, tostring(key))
883         table.insert(self.vallist, tostring(val))
884 end
885
886
887 -- DummyValue - This does nothing except being there
888 DummyValue = class(AbstractValue)
889
890 function DummyValue.__init__(self, map, ...)
891         AbstractValue.__init__(self, map, ...)
892         self.template = "cbi/dvalue"
893         self.value = nil
894 end
895
896 function DummyValue.parse(self)
897
898 end
899
900
901 --[[
902 Flag - A flag being enabled or disabled
903 ]]--
904 Flag = class(AbstractValue)
905
906 function Flag.__init__(self, ...)
907         AbstractValue.__init__(self, ...)
908         self.template  = "cbi/fvalue"
909
910         self.enabled = "1"
911         self.disabled = "0"
912 end
913
914 -- A flag can only have two states: set or unset
915 function Flag.parse(self, section)
916         local fvalue = self:formvalue(section)
917
918         if fvalue then
919                 fvalue = self.enabled
920         else
921                 fvalue = self.disabled
922         end
923
924         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
925                 if not(fvalue == self:cfgvalue(section)) then
926                         self:write(section, fvalue)
927                 end
928         else
929                 self:remove(section)
930         end
931 end
932
933
934
935 --[[
936 ListValue - A one-line value predefined in a list
937         widget: The widget that will be used (select, radio)
938 ]]--
939 ListValue = class(AbstractValue)
940
941 function ListValue.__init__(self, ...)
942         AbstractValue.__init__(self, ...)
943         self.template  = "cbi/lvalue"
944         self.keylist = {}
945         self.vallist = {}
946
947         self.size   = 1
948         self.widget = "select"
949 end
950
951 function ListValue.value(self, key, val, ...)
952         val = val or key
953         table.insert(self.keylist, tostring(key))
954         table.insert(self.vallist, tostring(val))
955         
956         for i, deps in ipairs({...}) do
957                 table.insert(self.deps, {add = "-"..key, deps=deps})
958         end
959 end
960
961 function ListValue.validate(self, val)
962         if luci.util.contains(self.keylist, val) then
963                 return val
964         else
965                 return nil
966         end
967 end
968
969
970
971 --[[
972 MultiValue - Multiple delimited values
973         widget: The widget that will be used (select, checkbox)
974         delimiter: The delimiter that will separate the values (default: " ")
975 ]]--
976 MultiValue = class(AbstractValue)
977
978 function MultiValue.__init__(self, ...)
979         AbstractValue.__init__(self, ...)
980         self.template = "cbi/mvalue"
981         self.keylist = {}
982         self.vallist = {}
983
984         self.widget = "checkbox"
985         self.delimiter = " "
986 end
987
988 function MultiValue.render(self, ...)
989         if self.widget == "select" and not self.size then
990                 self.size = #self.vallist
991         end
992
993         AbstractValue.render(self, ...)
994 end
995
996 function MultiValue.value(self, key, val)
997         val = val or key
998         table.insert(self.keylist, tostring(key))
999         table.insert(self.vallist, tostring(val))
1000 end
1001
1002 function MultiValue.valuelist(self, section)
1003         local val = self:cfgvalue(section)
1004
1005         if not(type(val) == "string") then
1006                 return {}
1007         end
1008
1009         return luci.util.split(val, self.delimiter)
1010 end
1011
1012 function MultiValue.validate(self, val)
1013         val = (type(val) == "table") and val or {val}
1014
1015         local result
1016
1017         for i, value in ipairs(val) do
1018                 if luci.util.contains(self.keylist, value) then
1019                         result = result and (result .. self.delimiter .. value) or value
1020                 end
1021         end
1022
1023         return result
1024 end
1025
1026
1027 StaticList = class(MultiValue)
1028
1029 function StaticList.__init__(self, ...)
1030         MultiValue.__init__(self, ...)
1031         self.cast = "table"
1032         self.valuelist = self.cfgvalue
1033 end
1034
1035 function StaticList.validate(self, value)
1036         value = (type(value) == "table") and value or {value}
1037
1038         local valid = {}
1039         for i, v in ipairs(value) do
1040                 if luci.util.contains(self.valuelist, v) then
1041                         table.insert(valid, v)
1042                 end
1043         end
1044         return valid
1045 end
1046
1047
1048 DynamicList = class(AbstractValue)
1049
1050 function DynamicList.__init__(self, ...)
1051         AbstractValue.__init__(self, ...)
1052         self.template  = "cbi/dynlist"
1053         self.cast = "table"
1054         
1055         self.keylist = {}
1056         self.vallist = {}
1057 end
1058
1059 function DynamicList.value(self, key, val)
1060         val = val or key
1061         table.insert(self.keylist, tostring(key))
1062         table.insert(self.vallist, tostring(val))
1063 end
1064
1065 function DynamicList.validate(self, value, section)
1066         value = (type(value) == "table") and value or {value}
1067         
1068         local valid = {}
1069         for i, v in ipairs(value) do
1070                 if v and #v > 0 and
1071                  not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1072                         table.insert(valid, v)
1073                 end
1074         end
1075         
1076         return valid
1077 end
1078
1079
1080 --[[
1081 TextValue - A multi-line value
1082         rows:   Rows
1083 ]]--
1084 TextValue = class(AbstractValue)
1085
1086 function TextValue.__init__(self, ...)
1087         AbstractValue.__init__(self, ...)
1088         self.template  = "cbi/tvalue"
1089 end
1090
1091 --[[
1092 Button
1093 ]]--
1094 Button = class(AbstractValue)
1095
1096 function Button.__init__(self, ...)
1097         AbstractValue.__init__(self, ...)
1098         self.template  = "cbi/button"
1099         self.inputstyle = nil
1100         self.rmempty = true
1101 end