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