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