libs/cbi: Added preliminary UCI list datatype support... for great justice
[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 self.cast = "string"
371
372 self.optional = true
373 self.addremove = false
374 self.dynamic = false
375 end
376
377 -- Appends a new option
378 function AbstractSection.option(self, class, option, ...)
379 if instanceof(class, AbstractValue) then
380 local obj = class(self.map, option, ...)
381
382 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
383
384 self:append(obj)
385 return obj
386 else
387 error("class must be a descendent of AbstractValue")
388 end
389 end
390
391 -- Parse optional options
392 function AbstractSection.parse_optionals(self, section)
393 if not self.optional then
394 return
395 end
396
397 self.optionals[section] = {}
398
399 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
400 for k,v in ipairs(self.children) do
401 if v.optional and not v:cfgvalue(section) then
402 if field == v.option then
403 field = nil
404 else
405 table.insert(self.optionals[section], v)
406 end
407 end
408 end
409
410 if field and #field > 0 and self.dynamic then
411 self:add_dynamic(field)
412 end
413 end
414
415 -- Add a dynamic option
416 function AbstractSection.add_dynamic(self, field, optional)
417 local o = self:option(Value, field, field)
418 o.optional = optional
419 end
420
421 -- Parse all dynamic options
422 function AbstractSection.parse_dynamic(self, section)
423 if not self.dynamic then
424 return
425 end
426
427 local arr = luci.util.clone(self:cfgvalue(section))
428 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
429 for k, v in pairs(form) do
430 arr[k] = v
431 end
432
433 for key,val in pairs(arr) do
434 local create = true
435
436 for i,c in ipairs(self.children) do
437 if c.option == key then
438 create = false
439 end
440 end
441
442 if create and key:sub(1, 1) ~= "." then
443 self:add_dynamic(key, true)
444 end
445 end
446 end
447
448 -- Returns the section's UCI table
449 function AbstractSection.cfgvalue(self, section)
450 local value = self.map:get(section)
451 if not self.cast or self.cast == type(value) then
452 return value
453 elseif self.cast == "string" then
454 if type(value) == "table" then
455 return value[1]
456 end
457 elseif self.cast == "table" then
458 return {value}
459 end
460 end
461
462 -- Removes the section
463 function AbstractSection.remove(self, section)
464 return self.map:del(section)
465 end
466
467 -- Creates the section
468 function AbstractSection.create(self, section)
469 local stat
470
471 if section then
472 stat = self.map:set(section, nil, self.sectiontype)
473 else
474 section = self.map:add(self.sectiontype)
475 stat = section
476 end
477
478 if stat then
479 for k,v in pairs(self.children) do
480 if v.default then
481 self.map:set(section, v.option, v.default)
482 end
483 end
484
485 for k,v in pairs(self.defaults) do
486 self.map:set(section, k, v)
487 end
488 end
489
490 return stat
491 end
492
493
494 SimpleSection = class(AbstractSection)
495
496 function SimpleSection.__init__(self, form, ...)
497 AbstractSection.__init__(self, form, nil, ...)
498 self.template = "cbi/nullsection"
499 end
500
501
502 Table = class(AbstractSection)
503
504 function Table.__init__(self, form, data, ...)
505 local datasource = {}
506 datasource.config = "table"
507 self.data = data
508
509 function datasource.get(self, section, option)
510 return data[section] and data[section][option]
511 end
512
513 function datasource.del(...)
514 return true
515 end
516
517 AbstractSection.__init__(self, datasource, "table", ...)
518 self.template = "cbi/tblsection"
519 self.rowcolors = true
520 self.anonymous = true
521 end
522
523 function Table.parse(self)
524 for i, k in ipairs(self:cfgsections()) do
525 if luci.http.formvalue("cbi.submit") then
526 Node.parse(self, k)
527 end
528 end
529 end
530
531 function Table.cfgsections(self)
532 local sections = {}
533
534 for i, v in luci.util.kspairs(self.data) do
535 table.insert(sections, i)
536 end
537
538 return sections
539 end
540
541
542
543 --[[
544 NamedSection - A fixed configuration section defined by its name
545 ]]--
546 NamedSection = class(AbstractSection)
547
548 function NamedSection.__init__(self, map, section, type, ...)
549 AbstractSection.__init__(self, map, type, ...)
550 Node._i18n(self, map.config, section, nil, ...)
551
552 self.template = "cbi/nsection"
553 self.section = section
554 self.addremove = false
555 end
556
557 function NamedSection.parse(self)
558 local s = self.section
559 local active = self:cfgvalue(s)
560
561
562 if self.addremove then
563 local path = self.config.."."..s
564 if active then -- Remove the section
565 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
566 return
567 end
568 else -- Create and apply default values
569 if luci.http.formvalue("cbi.cns."..path) then
570 self:create(s)
571 return
572 end
573 end
574 end
575
576 if active then
577 AbstractSection.parse_dynamic(self, s)
578 if luci.http.formvalue("cbi.submit") then
579 Node.parse(self, s)
580 end
581 AbstractSection.parse_optionals(self, s)
582 end
583 end
584
585
586 --[[
587 TypedSection - A (set of) configuration section(s) defined by the type
588 addremove: Defines whether the user can add/remove sections of this type
589 anonymous: Allow creating anonymous sections
590 validate: a validation function returning nil if the section is invalid
591 ]]--
592 TypedSection = class(AbstractSection)
593
594 function TypedSection.__init__(self, map, type, ...)
595 AbstractSection.__init__(self, map, type, ...)
596 Node._i18n(self, map.config, type, nil, ...)
597
598 self.template = "cbi/tsection"
599 self.deps = {}
600
601 self.anonymous = false
602 end
603
604 -- Return all matching UCI sections for this TypedSection
605 function TypedSection.cfgsections(self)
606 local sections = {}
607 uci.foreach(self.map.config, self.sectiontype,
608 function (section)
609 if self:checkscope(section[".name"]) then
610 table.insert(sections, section[".name"])
611 end
612 end)
613
614 return sections
615 end
616
617 -- Limits scope to sections that have certain option => value pairs
618 function TypedSection.depends(self, option, value)
619 table.insert(self.deps, {option=option, value=value})
620 end
621
622 function TypedSection.parse(self)
623 if self.addremove then
624 -- Create
625 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
626 local name = luci.http.formvalue(crval)
627 if self.anonymous then
628 if name then
629 self:create()
630 end
631 else
632 if name then
633 -- Ignore if it already exists
634 if self:cfgvalue(name) then
635 name = nil;
636 end
637
638 name = self:checkscope(name)
639
640 if not name then
641 self.err_invalid = true
642 end
643
644 if name and name:len() > 0 then
645 self:create(name)
646 end
647 end
648 end
649
650 -- Remove
651 crval = REMOVE_PREFIX .. self.config
652 name = luci.http.formvaluetable(crval)
653 for k,v in pairs(name) do
654 if self:cfgvalue(k) and self:checkscope(k) then
655 self:remove(k)
656 end
657 end
658 end
659
660 for i, k in ipairs(self:cfgsections()) do
661 AbstractSection.parse_dynamic(self, k)
662 if luci.http.formvalue("cbi.submit") then
663 Node.parse(self, k)
664 end
665 AbstractSection.parse_optionals(self, k)
666 end
667 end
668
669 -- Verifies scope of sections
670 function TypedSection.checkscope(self, section)
671 -- Check if we are not excluded
672 if self.filter and not self:filter(section) then
673 return nil
674 end
675
676 -- Check if at least one dependency is met
677 if #self.deps > 0 and self:cfgvalue(section) then
678 local stat = false
679
680 for k, v in ipairs(self.deps) do
681 if self:cfgvalue(section)[v.option] == v.value then
682 stat = true
683 end
684 end
685
686 if not stat then
687 return nil
688 end
689 end
690
691 return self:validate(section)
692 end
693
694
695 -- Dummy validate function
696 function TypedSection.validate(self, section)
697 return section
698 end
699
700
701 --[[
702 AbstractValue - An abstract Value Type
703 null: Value can be empty
704 valid: A function returning the value if it is valid otherwise nil
705 depends: A table of option => value pairs of which one must be true
706 default: The default value
707 size: The size of the input fields
708 rmempty: Unset value if empty
709 optional: This value is optional (see AbstractSection.optionals)
710 ]]--
711 AbstractValue = class(Node)
712
713 function AbstractValue.__init__(self, map, option, ...)
714 Node.__init__(self, ...)
715 self.option = option
716 self.map = map
717 self.config = map.config
718 self.tag_invalid = {}
719 self.tag_missing = {}
720 self.tag_error = {}
721 self.deps = {}
722
723 self.track_missing = false
724 self.rmempty = false
725 self.default = nil
726 self.size = nil
727 self.optional = false
728 end
729
730 -- Add a dependencie to another section field
731 function AbstractValue.depends(self, field, value)
732 local deps
733 if type(field) == "string" then
734 deps = {}
735 deps[field] = value
736 else
737 deps = field
738 end
739
740 table.insert(self.deps, {deps=deps, add=""})
741 end
742
743 -- Generates the unique CBID
744 function AbstractValue.cbid(self, section)
745 return "cbid."..self.map.config.."."..section.."."..self.option
746 end
747
748 -- Return whether this object should be created
749 function AbstractValue.formcreated(self, section)
750 local key = "cbi.opt."..self.config.."."..section
751 return (luci.http.formvalue(key) == self.option)
752 end
753
754 -- Returns the formvalue for this object
755 function AbstractValue.formvalue(self, section)
756 return luci.http.formvalue(self:cbid(section))
757 end
758
759 function AbstractValue.additional(self, value)
760 self.optional = value
761 end
762
763 function AbstractValue.mandatory(self, value)
764 self.rmempty = not value
765 end
766
767 function AbstractValue.parse(self, section)
768 local fvalue = self:formvalue(section)
769 local cvalue = self:cfgvalue(section)
770
771 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
772 fvalue = self:transform(self:validate(fvalue, section))
773 if not fvalue then
774 self.tag_invalid[section] = true
775 end
776 if fvalue and not (fvalue == cvalue) then
777 self:write(section, fvalue)
778 end
779 else -- Unset the UCI or error
780 if self.rmempty or self.optional then
781 self:remove(section)
782 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
783 self.tag_missing[section] = true
784 end
785 end
786 end
787
788 -- Render if this value exists or if it is mandatory
789 function AbstractValue.render(self, s, scope)
790 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
791 scope = scope or {}
792 scope.section = s
793 scope.cbid = self:cbid(s)
794
795 scope.ifattr = function(cond,key,val)
796 if cond then
797 return string.format(
798 ' %s="%s"', tostring(key),
799 luci.util.pcdata(tostring( val
800 or scope[key]
801 or (type(self[key]) ~= "function" and self[key])
802 or "" ))
803 )
804 else
805 return ''
806 end
807 end
808
809 scope.attr = function(...)
810 return scope.ifattr( true, ... )
811 end
812
813 Node.render(self, scope)
814 end
815 end
816
817 -- Return the UCI value of this object
818 function AbstractValue.cfgvalue(self, section)
819 return self.map:get(section, self.option)
820 end
821
822 -- Validate the form value
823 function AbstractValue.validate(self, value)
824 return value
825 end
826
827 AbstractValue.transform = AbstractValue.validate
828
829
830 -- Write to UCI
831 function AbstractValue.write(self, section, value)
832 return self.map:set(section, self.option, value)
833 end
834
835 -- Remove from UCI
836 function AbstractValue.remove(self, section)
837 return self.map:del(section, self.option)
838 end
839
840
841
842
843 --[[
844 Value - A one-line value
845 maxlength: The maximum length
846 ]]--
847 Value = class(AbstractValue)
848
849 function Value.__init__(self, ...)
850 AbstractValue.__init__(self, ...)
851 self.template = "cbi/value"
852 self.keylist = {}
853 self.vallist = {}
854 end
855
856 function Value.value(self, key, val)
857 val = val or key
858 table.insert(self.keylist, tostring(key))
859 table.insert(self.vallist, tostring(val))
860 end
861
862
863 -- DummyValue - This does nothing except being there
864 DummyValue = class(AbstractValue)
865
866 function DummyValue.__init__(self, map, ...)
867 AbstractValue.__init__(self, map, ...)
868 self.template = "cbi/dvalue"
869 self.value = nil
870 end
871
872 function DummyValue.parse(self)
873
874 end
875
876
877 --[[
878 Flag - A flag being enabled or disabled
879 ]]--
880 Flag = class(AbstractValue)
881
882 function Flag.__init__(self, ...)
883 AbstractValue.__init__(self, ...)
884 self.template = "cbi/fvalue"
885
886 self.enabled = "1"
887 self.disabled = "0"
888 end
889
890 -- A flag can only have two states: set or unset
891 function Flag.parse(self, section)
892 local fvalue = self:formvalue(section)
893
894 if fvalue then
895 fvalue = self.enabled
896 else
897 fvalue = self.disabled
898 end
899
900 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
901 if not(fvalue == self:cfgvalue(section)) then
902 self:write(section, fvalue)
903 end
904 else
905 self:remove(section)
906 end
907 end
908
909
910
911 --[[
912 ListValue - A one-line value predefined in a list
913 widget: The widget that will be used (select, radio)
914 ]]--
915 ListValue = class(AbstractValue)
916
917 function ListValue.__init__(self, ...)
918 AbstractValue.__init__(self, ...)
919 self.template = "cbi/lvalue"
920 self.keylist = {}
921 self.vallist = {}
922
923 self.size = 1
924 self.widget = "select"
925 end
926
927 function ListValue.value(self, key, val, ...)
928 val = val or key
929 table.insert(self.keylist, tostring(key))
930 table.insert(self.vallist, tostring(val))
931
932 for i, deps in ipairs({...}) do
933 table.insert(self.deps, {add = "-"..key, deps=deps})
934 end
935 end
936
937 function ListValue.validate(self, val)
938 if luci.util.contains(self.keylist, val) then
939 return val
940 else
941 return nil
942 end
943 end
944
945
946
947 --[[
948 MultiValue - Multiple delimited values
949 widget: The widget that will be used (select, checkbox)
950 delimiter: The delimiter that will separate the values (default: " ")
951 ]]--
952 MultiValue = class(AbstractValue)
953
954 function MultiValue.__init__(self, ...)
955 AbstractValue.__init__(self, ...)
956 self.template = "cbi/mvalue"
957 self.keylist = {}
958 self.vallist = {}
959
960 self.widget = "checkbox"
961 self.delimiter = " "
962 end
963
964 function MultiValue.render(self, ...)
965 if self.widget == "select" and not self.size then
966 self.size = #self.vallist
967 end
968
969 AbstractValue.render(self, ...)
970 end
971
972 function MultiValue.value(self, key, val)
973 val = val or key
974 table.insert(self.keylist, tostring(key))
975 table.insert(self.vallist, tostring(val))
976 end
977
978 function MultiValue.valuelist(self, section)
979 local val = self:cfgvalue(section)
980
981 if not(type(val) == "string") then
982 return {}
983 end
984
985 return luci.util.split(val, self.delimiter)
986 end
987
988 function MultiValue.validate(self, val)
989 val = (type(val) == "table") and val or {val}
990
991 local result
992
993 for i, value in ipairs(val) do
994 if luci.util.contains(self.keylist, value) then
995 result = result and (result .. self.delimiter .. value) or value
996 end
997 end
998
999 return result
1000 end
1001
1002
1003 StaticList = class(MultiValue)
1004
1005 function StaticList.__init__(self, ...)
1006 MultiValue.__init__(self, ...)
1007 self.cast = "table"
1008 self.valuelist = self.cfgvalue
1009 end
1010
1011 function StaticList.validate(self, value)
1012 value = (type(value) == "table") and value or {value}
1013
1014 local valid = {}
1015 for i, v in ipairs(value) do
1016 if luci.util.contains(self.valuelist, v) then
1017 table.insert(valid, v)
1018 end
1019 end
1020 return valid
1021 end
1022
1023
1024 DynamicList = class(AbstractValue)
1025
1026 function DynamicList.__init__(self, ...)
1027 AbstractValue.__init__(self, ...)
1028 self.template = "cbi/dynlist"
1029 self.cast = "table"
1030
1031 self.keylist = {}
1032 self.vallist = {}
1033 end
1034
1035 function DynamicList.value(self, key, val)
1036 val = val or key
1037 table.insert(self.keylist, tostring(key))
1038 table.insert(self.vallist, tostring(val))
1039 end
1040
1041 function DynamicList.validate(self, value, section)
1042 value = (type(value) == "table") and value or {value}
1043
1044 local valid = {}
1045 for i, v in ipairs(value) do
1046 if v and #v > 0 and
1047 not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1048 table.insert(valid, v)
1049 end
1050 end
1051
1052 return valid
1053 end
1054
1055
1056 --[[
1057 TextValue - A multi-line value
1058 rows: Rows
1059 ]]--
1060 TextValue = class(AbstractValue)
1061
1062 function TextValue.__init__(self, ...)
1063 AbstractValue.__init__(self, ...)
1064 self.template = "cbi/tvalue"
1065 end
1066
1067 --[[
1068 Button
1069 ]]--
1070 Button = class(AbstractValue)
1071
1072 function Button.__init__(self, ...)
1073 AbstractValue.__init__(self, ...)
1074 self.template = "cbi/button"
1075 self.inputstyle = nil
1076 self.rmempty = true
1077 end