* 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