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