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