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