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