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