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