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