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