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