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