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