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