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