543c895af4e3c3daf4c5f07e0b05e0e457e130df
[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                 -- Create
703                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
704                 local name  = luci.http.formvalue(crval)
705                 if self.anonymous then
706                         if name then
707                                 self:create()
708                         end
709                 else
710                         if name then
711                                 -- Ignore if it already exists
712                                 if self:cfgvalue(name) then
713                                         name = nil;
714                                 end
715
716                                 name = self:checkscope(name)
717
718                                 if not name then
719                                         self.err_invalid = true
720                                 end
721
722                                 if name and #name > 0 then
723                                         self:create(name)
724                                 end
725                         end
726                 end
727
728                 -- Remove
729                 crval = REMOVE_PREFIX .. self.config
730                 name = luci.http.formvaluetable(crval)
731                 for k,v in pairs(name) do
732                         if self:cfgvalue(k) and self:checkscope(k) then
733                                 self:remove(k)
734                         end
735                 end
736         end
737
738         local co
739         for i, k in ipairs(self:cfgsections()) do
740                 AbstractSection.parse_dynamic(self, k)
741                 if luci.http.formvalue("cbi.submit") then
742                         Node.parse(self, k)
743                         
744                         if not self.override_scheme and self.map.scheme then
745                                 local co = self.map:get()
746                                 local stat, err = self.map.validator:validate_section(self.config, k, co)
747                                 if err then
748                                         self.map.save = false
749                                         if err.code == luci.uvl.errors.ERR_DEPENDENCY then
750                                                 self.tag_deperror[k] = true
751                                         else
752                                                 self.tag_invalid[k] = true
753                                         end
754                                         for i, v in ipairs(err.childs) do
755                                                 if v.option and self.fields[v.option] then
756                                                         if v.code == luci.uvl.errors.ERR_DEPENDENCY then
757                                                                 self.fields[v.option].tag_reqerror[k] = true
758                                                         elseif v.code == luci.uvl.errors.ERR_OPTION then
759                                                                 self.fields[v.option].tag_invalid[k] = true
760                                                         end
761                                                 end
762                                         end
763                                 end
764                         end
765                 end
766                 AbstractSection.parse_optionals(self, k)
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
842                 if vs.depends and not self.override_dependencies then
843                         for i, deps in ipairs(vs.depends) do
844                                 deps = _uvl_strip_remote_dependencies(deps)
845                                 if next(deps) then
846                                         self:depends(deps)
847                                 end
848                         end
849                 end
850         end
851 end
852
853 -- Add a dependencie to another section field
854 function AbstractValue.depends(self, field, value)
855         local deps
856         if type(field) == "string" then
857                 deps = {}
858                 deps[field] = value
859         else
860                 deps = field
861         end
862
863         table.insert(self.deps, {deps=deps, add=""})
864 end
865
866 -- Generates the unique CBID
867 function AbstractValue.cbid(self, section)
868         return "cbid."..self.map.config.."."..section.."."..self.option
869 end
870
871 -- Return whether this object should be created
872 function AbstractValue.formcreated(self, section)
873         local key = "cbi.opt."..self.config.."."..section
874         return (luci.http.formvalue(key) == self.option)
875 end
876
877 -- Returns the formvalue for this object
878 function AbstractValue.formvalue(self, section)
879         return luci.http.formvalue(self:cbid(section))
880 end
881
882 function AbstractValue.additional(self, value)
883         self.optional = value
884 end
885
886 function AbstractValue.mandatory(self, value)
887         self.rmempty = not value
888 end
889
890 function AbstractValue.parse(self, section)
891         local fvalue = self:formvalue(section)
892         local cvalue = self:cfgvalue(section)
893
894         if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
895                 fvalue = self:transform(self:validate(fvalue, section))
896                 if not fvalue then
897                         self.tag_invalid[section] = true
898                 end
899                 if fvalue and not (fvalue == cvalue) then
900                         self:write(section, fvalue)
901                 end
902         else                                                    -- Unset the UCI or error
903                 if self.rmempty or self.optional then
904                         self:remove(section)
905                 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
906                         self.tag_missing[section] = true
907                 end
908         end
909 end
910
911 -- Render if this value exists or if it is mandatory
912 function AbstractValue.render(self, s, scope)
913         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
914                 scope = scope or {}
915                 scope.section   = s
916                 scope.cbid      = self:cbid(s)
917                 scope.striptags = luci.util.striptags
918
919                 scope.ifattr = function(cond,key,val)
920                         if cond then
921                                 return string.format(
922                                         ' %s="%s"', tostring(key),
923                                         luci.util.pcdata(tostring( val
924                                          or scope[key]
925                                          or (type(self[key]) ~= "function" and self[key])
926                                          or "" ))
927                                 )
928                         else
929                                 return ''
930                         end
931                 end
932
933                 scope.attr = function(...)
934                         return scope.ifattr( true, ... )
935                 end
936
937                 Node.render(self, scope)
938         end
939 end
940
941 -- Return the UCI value of this object
942 function AbstractValue.cfgvalue(self, section)
943         local value = self.map:get(section, self.option)
944         if not self.cast or self.cast == type(value) then
945                 return value
946         elseif self.cast == "string" then
947                 if type(value) == "table" then
948                         return value[1]
949                 end
950         elseif self.cast == "table" then
951                 return {value}
952         end
953 end
954
955 -- Validate the form value
956 function AbstractValue.validate(self, value)
957         return value
958 end
959
960 AbstractValue.transform = AbstractValue.validate
961
962
963 -- Write to UCI
964 function AbstractValue.write(self, section, value)
965         return self.map:set(section, self.option, value)
966 end
967
968 -- Remove from UCI
969 function AbstractValue.remove(self, section)
970         return self.map:del(section, self.option)
971 end
972
973
974
975
976 --[[
977 Value - A one-line value
978         maxlength:      The maximum length
979 ]]--
980 Value = class(AbstractValue)
981
982 function Value.__init__(self, ...)
983         AbstractValue.__init__(self, ...)
984         self.template  = "cbi/value"
985         self.keylist = {}
986         self.vallist = {}
987 end
988
989 function Value.value(self, key, val)
990         val = val or key
991         table.insert(self.keylist, tostring(key))
992         table.insert(self.vallist, tostring(val))
993 end
994
995
996 -- DummyValue - This does nothing except being there
997 DummyValue = class(AbstractValue)
998
999 function DummyValue.__init__(self, ...)
1000         AbstractValue.__init__(self, ...)
1001         self.template = "cbi/dvalue"
1002         self.value = nil
1003 end
1004
1005 function DummyValue.parse(self)
1006
1007 end
1008
1009
1010 --[[
1011 Flag - A flag being enabled or disabled
1012 ]]--
1013 Flag = class(AbstractValue)
1014
1015 function Flag.__init__(self, ...)
1016         AbstractValue.__init__(self, ...)
1017         self.template  = "cbi/fvalue"
1018
1019         self.enabled = "1"
1020         self.disabled = "0"
1021 end
1022
1023 -- A flag can only have two states: set or unset
1024 function Flag.parse(self, section)
1025         local fvalue = self:formvalue(section)
1026
1027         if fvalue then
1028                 fvalue = self.enabled
1029         else
1030                 fvalue = self.disabled
1031         end
1032
1033         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1034                 if not(fvalue == self:cfgvalue(section)) then
1035                         self:write(section, fvalue)
1036                 end
1037         else
1038                 self:remove(section)
1039         end
1040 end
1041
1042
1043
1044 --[[
1045 ListValue - A one-line value predefined in a list
1046         widget: The widget that will be used (select, radio)
1047 ]]--
1048 ListValue = class(AbstractValue)
1049
1050 function ListValue.__init__(self, ...)
1051         AbstractValue.__init__(self, ...)
1052         self.template  = "cbi/lvalue"
1053
1054         self.keylist = {}
1055         self.vallist = {}
1056         self.size   = 1
1057         self.widget = "select"
1058
1059         if not self.override_scheme
1060          and self.map:get_scheme(self.section.sectiontype, self.option) then
1061                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1062                 if self.value and vs.values and not self.override_values then
1063                         if self.rmempty or self.optional then
1064                                 self:value("")
1065                         end
1066                         for k, v in pairs(vs.values) do
1067                                 local deps = {}
1068                                 if vs.enum_depends and vs.enum_depends[k] then
1069                                         for i, dep in ipairs(vs.enum_depends[k]) do
1070                                                 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1071                                         end
1072                                 end
1073                                 self:value(k, v, unpack(deps))
1074                         end
1075                 end
1076         end
1077 end
1078
1079 function ListValue.value(self, key, val, ...)
1080         val = val or key
1081         table.insert(self.keylist, tostring(key))
1082         table.insert(self.vallist, tostring(val))
1083
1084         for i, deps in ipairs({...}) do
1085                 table.insert(self.deps, {add = "-"..key, deps=deps})
1086         end
1087 end
1088
1089 function ListValue.validate(self, val)
1090         if luci.util.contains(self.keylist, val) then
1091                 return val
1092         else
1093                 return nil
1094         end
1095 end
1096
1097
1098
1099 --[[
1100 MultiValue - Multiple delimited values
1101         widget: The widget that will be used (select, checkbox)
1102         delimiter: The delimiter that will separate the values (default: " ")
1103 ]]--
1104 MultiValue = class(AbstractValue)
1105
1106 function MultiValue.__init__(self, ...)
1107         AbstractValue.__init__(self, ...)
1108         self.template = "cbi/mvalue"
1109
1110         self.keylist = {}
1111         self.vallist = {}
1112
1113         self.widget = "checkbox"
1114         self.delimiter = " "
1115 end
1116
1117 function MultiValue.render(self, ...)
1118         if self.widget == "select" and not self.size then
1119                 self.size = #self.vallist
1120         end
1121
1122         AbstractValue.render(self, ...)
1123 end
1124
1125 function MultiValue.value(self, key, val)
1126         val = val or key
1127         table.insert(self.keylist, tostring(key))
1128         table.insert(self.vallist, tostring(val))
1129 end
1130
1131 function MultiValue.valuelist(self, section)
1132         local val = self:cfgvalue(section)
1133
1134         if not(type(val) == "string") then
1135                 return {}
1136         end
1137
1138         return luci.util.split(val, self.delimiter)
1139 end
1140
1141 function MultiValue.validate(self, val)
1142         val = (type(val) == "table") and val or {val}
1143
1144         local result
1145
1146         for i, value in ipairs(val) do
1147                 if luci.util.contains(self.keylist, value) then
1148                         result = result and (result .. self.delimiter .. value) or value
1149                 end
1150         end
1151
1152         return result
1153 end
1154
1155
1156 StaticList = class(MultiValue)
1157
1158 function StaticList.__init__(self, ...)
1159         MultiValue.__init__(self, ...)
1160         self.cast = "table"
1161         self.valuelist = self.cfgvalue
1162
1163         if not self.override_scheme
1164          and self.map:get_scheme(self.section.sectiontype, self.option) then
1165                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1166                 if self.value and vs.values and not self.override_values then
1167                         for k, v in pairs(vs.values) do
1168                                 self:value(k, v)
1169                         end
1170                 end
1171         end
1172 end
1173
1174 function StaticList.validate(self, value)
1175         value = (type(value) == "table") and value or {value}
1176
1177         local valid = {}
1178         for i, v in ipairs(value) do
1179                 if luci.util.contains(self.valuelist, v) then
1180                         table.insert(valid, v)
1181                 end
1182         end
1183         return valid
1184 end
1185
1186
1187 DynamicList = class(AbstractValue)
1188
1189 function DynamicList.__init__(self, ...)
1190         AbstractValue.__init__(self, ...)
1191         self.template  = "cbi/dynlist"
1192         self.cast = "table"
1193         self.keylist = {}
1194         self.vallist = {}
1195 end
1196
1197 function DynamicList.value(self, key, val)
1198         val = val or key
1199         table.insert(self.keylist, tostring(key))
1200         table.insert(self.vallist, tostring(val))
1201 end
1202
1203 function DynamicList.validate(self, value, section)
1204         value = (type(value) == "table") and value or {value}
1205
1206         local valid = {}
1207         for i, v in ipairs(value) do
1208                 if v and #v > 0 and
1209                  not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1210                         table.insert(valid, v)
1211                 end
1212         end
1213
1214         return valid
1215 end
1216
1217
1218 --[[
1219 TextValue - A multi-line value
1220         rows:   Rows
1221 ]]--
1222 TextValue = class(AbstractValue)
1223
1224 function TextValue.__init__(self, ...)
1225         AbstractValue.__init__(self, ...)
1226         self.template  = "cbi/tvalue"
1227 end
1228
1229 --[[
1230 Button
1231 ]]--
1232 Button = class(AbstractValue)
1233
1234 function Button.__init__(self, ...)
1235         AbstractValue.__init__(self, ...)
1236         self.template  = "cbi/button"
1237         self.inputstyle = nil
1238         self.rmempty = true
1239 end