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