* luci/libs: use striptags() in cbi <option> elements
[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                         luci.util.perror(k)
645                         luci.util.perror(self:cfgvalue(k))
646                         luci.util.perror(self:checkscope(k))
647                         if self:cfgvalue(k) and self:checkscope(k) then
648                                 self:remove(k)
649                         end
650                 end
651         end
652
653         for i, k in ipairs(self:cfgsections()) do
654                 AbstractSection.parse_dynamic(self, k)
655                 if luci.http.formvalue("cbi.submit") then
656                         Node.parse(self, k)
657                 end
658                 AbstractSection.parse_optionals(self, k)
659         end
660 end
661
662 -- Verifies scope of sections
663 function TypedSection.checkscope(self, section)
664         -- Check if we are not excluded
665         if self.filter and not self:filter(section) then
666                 return nil
667         end
668
669         -- Check if at least one dependency is met
670         if #self.deps > 0 and self:cfgvalue(section) then
671                 local stat = false
672
673                 for k, v in ipairs(self.deps) do
674                         if self:cfgvalue(section)[v.option] == v.value then
675                                 stat = true
676                         end
677                 end
678
679                 if not stat then
680                         return nil
681                 end
682         end
683
684         return self:validate(section)
685 end
686
687
688 -- Dummy validate function
689 function TypedSection.validate(self, section)
690         return section
691 end
692
693
694 --[[
695 AbstractValue - An abstract Value Type
696         null:           Value can be empty
697         valid:          A function returning the value if it is valid otherwise nil
698         depends:        A table of option => value pairs of which one must be true
699         default:        The default value
700         size:           The size of the input fields
701         rmempty:        Unset value if empty
702         optional:       This value is optional (see AbstractSection.optionals)
703 ]]--
704 AbstractValue = class(Node)
705
706 function AbstractValue.__init__(self, map, option, ...)
707         Node.__init__(self, ...)
708         self.option = option
709         self.map    = map
710         self.config = map.config
711         self.tag_invalid = {}
712         self.tag_missing = {}
713         self.tag_error = {}
714         self.deps = {}
715         self.cast = "string"
716
717         self.track_missing = false
718         self.rmempty   = false
719         self.default   = nil
720         self.size      = nil
721         self.optional  = false
722 end
723
724 -- Add a dependencie to another section field
725 function AbstractValue.depends(self, field, value)
726         local deps
727         if type(field) == "string" then
728                 deps = {}
729                 deps[field] = value
730         else
731                 deps = field
732         end
733         
734         table.insert(self.deps, {deps=deps, add=""})
735 end
736
737 -- Generates the unique CBID
738 function AbstractValue.cbid(self, section)
739         return "cbid."..self.map.config.."."..section.."."..self.option
740 end
741
742 -- Return whether this object should be created
743 function AbstractValue.formcreated(self, section)
744         local key = "cbi.opt."..self.config.."."..section
745         return (luci.http.formvalue(key) == self.option)
746 end
747
748 -- Returns the formvalue for this object
749 function AbstractValue.formvalue(self, section)
750         return luci.http.formvalue(self:cbid(section))
751 end
752
753 function AbstractValue.additional(self, value)
754         self.optional = value
755 end
756
757 function AbstractValue.mandatory(self, value)
758         self.rmempty = not value
759 end
760
761 function AbstractValue.parse(self, section)
762         local fvalue = self:formvalue(section)
763         local cvalue = self:cfgvalue(section)
764
765         if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
766                 fvalue = self:transform(self:validate(fvalue, section))
767                 if not fvalue then
768                         self.tag_invalid[section] = true
769                 end
770                 if fvalue and not (fvalue == cvalue) then
771                         self:write(section, fvalue)
772                 end
773         else                                                    -- Unset the UCI or error
774                 if self.rmempty or self.optional then
775                         self:remove(section)
776                 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
777                         self.tag_missing[section] = true
778                 end
779         end
780 end
781
782 -- Render if this value exists or if it is mandatory
783 function AbstractValue.render(self, s, scope)
784         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
785                 scope = scope or {}
786                 scope.section   = s
787                 scope.cbid      = self:cbid(s)
788                 scope.striptags = luci.util.striptags
789
790                 scope.ifattr = function(cond,key,val)
791                         if cond then
792                                 return string.format(
793                                         ' %s="%s"', tostring(key),
794                                         luci.util.pcdata(tostring( val
795                                          or scope[key]
796                                          or (type(self[key]) ~= "function" and self[key])
797                                          or "" ))
798                                 )
799                         else
800                                 return ''
801                         end
802                 end
803
804                 scope.attr = function(...)
805                         return scope.ifattr( true, ... )
806                 end
807
808                 Node.render(self, scope)
809         end
810 end
811
812 -- Return the UCI value of this object
813 function AbstractValue.cfgvalue(self, section)
814         local value = self.map:get(section, self.option)
815         if not self.cast or self.cast == type(value) then
816                 return value
817         elseif self.cast == "string" then
818                 if type(value) == "table" then
819                         return value[1]
820                 end
821         elseif self.cast == "table" then
822                 return {value}
823         end
824 end
825
826 -- Validate the form value
827 function AbstractValue.validate(self, value)
828         return value
829 end
830
831 AbstractValue.transform = AbstractValue.validate
832
833
834 -- Write to UCI
835 function AbstractValue.write(self, section, value)
836         return self.map:set(section, self.option, value)
837 end
838
839 -- Remove from UCI
840 function AbstractValue.remove(self, section)
841         return self.map:del(section, self.option)
842 end
843
844
845
846
847 --[[
848 Value - A one-line value
849         maxlength:      The maximum length
850 ]]--
851 Value = class(AbstractValue)
852
853 function Value.__init__(self, ...)
854         AbstractValue.__init__(self, ...)
855         self.template  = "cbi/value"
856         self.keylist = {}
857         self.vallist = {}
858 end
859
860 function Value.value(self, key, val)
861         val = val or key
862         table.insert(self.keylist, tostring(key))
863         table.insert(self.vallist, tostring(val))
864 end
865
866
867 -- DummyValue - This does nothing except being there
868 DummyValue = class(AbstractValue)
869
870 function DummyValue.__init__(self, map, ...)
871         AbstractValue.__init__(self, map, ...)
872         self.template = "cbi/dvalue"
873         self.value = nil
874 end
875
876 function DummyValue.parse(self)
877
878 end
879
880
881 --[[
882 Flag - A flag being enabled or disabled
883 ]]--
884 Flag = class(AbstractValue)
885
886 function Flag.__init__(self, ...)
887         AbstractValue.__init__(self, ...)
888         self.template  = "cbi/fvalue"
889
890         self.enabled = "1"
891         self.disabled = "0"
892 end
893
894 -- A flag can only have two states: set or unset
895 function Flag.parse(self, section)
896         local fvalue = self:formvalue(section)
897
898         if fvalue then
899                 fvalue = self.enabled
900         else
901                 fvalue = self.disabled
902         end
903
904         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
905                 if not(fvalue == self:cfgvalue(section)) then
906                         self:write(section, fvalue)
907                 end
908         else
909                 self:remove(section)
910         end
911 end
912
913
914
915 --[[
916 ListValue - A one-line value predefined in a list
917         widget: The widget that will be used (select, radio)
918 ]]--
919 ListValue = class(AbstractValue)
920
921 function ListValue.__init__(self, ...)
922         AbstractValue.__init__(self, ...)
923         self.template  = "cbi/lvalue"
924         self.keylist = {}
925         self.vallist = {}
926
927         self.size   = 1
928         self.widget = "select"
929 end
930
931 function ListValue.value(self, key, val, ...)
932         val = val or key
933         table.insert(self.keylist, tostring(key))
934         table.insert(self.vallist, tostring(val))
935         
936         for i, deps in ipairs({...}) do
937                 table.insert(self.deps, {add = "-"..key, deps=deps})
938         end
939 end
940
941 function ListValue.validate(self, val)
942         if luci.util.contains(self.keylist, val) then
943                 return val
944         else
945                 return nil
946         end
947 end
948
949
950
951 --[[
952 MultiValue - Multiple delimited values
953         widget: The widget that will be used (select, checkbox)
954         delimiter: The delimiter that will separate the values (default: " ")
955 ]]--
956 MultiValue = class(AbstractValue)
957
958 function MultiValue.__init__(self, ...)
959         AbstractValue.__init__(self, ...)
960         self.template = "cbi/mvalue"
961         self.keylist = {}
962         self.vallist = {}
963
964         self.widget = "checkbox"
965         self.delimiter = " "
966 end
967
968 function MultiValue.render(self, ...)
969         if self.widget == "select" and not self.size then
970                 self.size = #self.vallist
971         end
972
973         AbstractValue.render(self, ...)
974 end
975
976 function MultiValue.value(self, key, val)
977         val = val or key
978         table.insert(self.keylist, tostring(key))
979         table.insert(self.vallist, tostring(val))
980 end
981
982 function MultiValue.valuelist(self, section)
983         local val = self:cfgvalue(section)
984
985         if not(type(val) == "string") then
986                 return {}
987         end
988
989         return luci.util.split(val, self.delimiter)
990 end
991
992 function MultiValue.validate(self, val)
993         val = (type(val) == "table") and val or {val}
994
995         local result
996
997         for i, value in ipairs(val) do
998                 if luci.util.contains(self.keylist, value) then
999                         result = result and (result .. self.delimiter .. value) or value
1000                 end
1001         end
1002
1003         return result
1004 end
1005
1006
1007 StaticList = class(MultiValue)
1008
1009 function StaticList.__init__(self, ...)
1010         MultiValue.__init__(self, ...)
1011         self.cast = "table"
1012         self.valuelist = self.cfgvalue
1013 end
1014
1015 function StaticList.validate(self, value)
1016         value = (type(value) == "table") and value or {value}
1017
1018         local valid = {}
1019         for i, v in ipairs(value) do
1020                 if luci.util.contains(self.valuelist, v) then
1021                         table.insert(valid, v)
1022                 end
1023         end
1024         return valid
1025 end
1026
1027
1028 DynamicList = class(AbstractValue)
1029
1030 function DynamicList.__init__(self, ...)
1031         AbstractValue.__init__(self, ...)
1032         self.template  = "cbi/dynlist"
1033         self.cast = "table"
1034         
1035         self.keylist = {}
1036         self.vallist = {}
1037 end
1038
1039 function DynamicList.value(self, key, val)
1040         val = val or key
1041         table.insert(self.keylist, tostring(key))
1042         table.insert(self.vallist, tostring(val))
1043 end
1044
1045 function DynamicList.validate(self, value, section)
1046         value = (type(value) == "table") and value or {value}
1047         
1048         local valid = {}
1049         for i, v in ipairs(value) do
1050                 if v and #v > 0 and
1051                  not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1052                         table.insert(valid, v)
1053                 end
1054         end
1055         
1056         return valid
1057 end
1058
1059
1060 --[[
1061 TextValue - A multi-line value
1062         rows:   Rows
1063 ]]--
1064 TextValue = class(AbstractValue)
1065
1066 function TextValue.__init__(self, ...)
1067         AbstractValue.__init__(self, ...)
1068         self.template  = "cbi/tvalue"
1069 end
1070
1071 --[[
1072 Button
1073 ]]--
1074 Button = class(AbstractValue)
1075
1076 function Button.__init__(self, ...)
1077         AbstractValue.__init__(self, ...)
1078         self.template  = "cbi/button"
1079         self.inputstyle = nil
1080         self.rmempty = true
1081 end