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