libs/cbi: Added support for enum dependencies
[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 function SimpleForm.get_scheme()
388         return nil
389 end
390
391
392
393 --[[
394 AbstractSection
395 ]]--
396 AbstractSection = class(Node)
397
398 function AbstractSection.__init__(self, map, sectiontype, ...)
399         Node.__init__(self, ...)
400         self.sectiontype = sectiontype
401         self.map = map
402         self.config = map.config
403         self.optionals = {}
404         self.defaults = {}
405
406         self.optional = true
407         self.addremove = false
408         self.dynamic = false
409 end
410
411 -- Appends a new option
412 function AbstractSection.option(self, class, option, ...)
413         -- Autodetect from UVL
414         if class == true and self.map:get_scheme(self.sectiontype, option) then
415                 local vs = self.map:get_scheme(self.sectiontype, option)
416                 if vs.type == "boolean" then
417                         class = Flag
418                 elseif vs.type == "list" then
419                         class = DynamicList
420                 elseif vs.type == "enum" or vs.type == "reference" then
421                         class = ListValue
422                 else
423                         class = Value
424                 end
425         end
426
427         if instanceof(class, AbstractValue) then
428                 local obj  = class(self.map, self, option, ...)
429
430                 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
431
432                 self:append(obj)
433                 return obj
434         elseif class == true then
435                 error("No valid class was given and autodetection failed.")
436         else
437                 error("class must be a descendant of AbstractValue")
438         end
439 end
440
441 -- Parse optional options
442 function AbstractSection.parse_optionals(self, section)
443         if not self.optional then
444                 return
445         end
446
447         self.optionals[section] = {}
448
449         local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
450         for k,v in ipairs(self.children) do
451                 if v.optional and not v:cfgvalue(section) then
452                         if field == v.option then
453                                 field = nil
454                         else
455                                 table.insert(self.optionals[section], v)
456                         end
457                 end
458         end
459
460         if field and #field > 0 and self.dynamic then
461                 self:add_dynamic(field)
462         end
463 end
464
465 -- Add a dynamic option
466 function AbstractSection.add_dynamic(self, field, optional)
467         local o = self:option(Value, field, field)
468         o.optional = optional
469 end
470
471 -- Parse all dynamic options
472 function AbstractSection.parse_dynamic(self, section)
473         if not self.dynamic then
474                 return
475         end
476
477         local arr  = luci.util.clone(self:cfgvalue(section))
478         local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
479         for k, v in pairs(form) do
480                 arr[k] = v
481         end
482
483         for key,val in pairs(arr) do
484                 local create = true
485
486                 for i,c in ipairs(self.children) do
487                         if c.option == key then
488                                 create = false
489                         end
490                 end
491
492                 if create and key:sub(1, 1) ~= "." then
493                         self:add_dynamic(key, true)
494                 end
495         end
496 end
497
498 -- Returns the section's UCI table
499 function AbstractSection.cfgvalue(self, section)
500         return self.map:get(section)
501 end
502
503 -- Removes the section
504 function AbstractSection.remove(self, section)
505         return self.map:del(section)
506 end
507
508 -- Creates the section
509 function AbstractSection.create(self, section)
510         local stat
511
512         if section then
513                 stat = self.map:set(section, nil, self.sectiontype)
514         else
515                 section = self.map:add(self.sectiontype)
516                 stat = section
517         end
518
519         if stat then
520                 for k,v in pairs(self.children) do
521                         if v.default then
522                                 self.map:set(section, v.option, v.default)
523                         end
524                 end
525
526                 for k,v in pairs(self.defaults) do
527                         self.map:set(section, k, v)
528                 end
529         end
530
531         return stat
532 end
533
534
535 SimpleSection = class(AbstractSection)
536
537 function SimpleSection.__init__(self, form, ...)
538         AbstractSection.__init__(self, form, nil, ...)
539         self.template = "cbi/nullsection"
540 end
541
542
543 Table = class(AbstractSection)
544
545 function Table.__init__(self, form, data, ...)
546         local datasource = {}
547         datasource.config = "table"
548         self.data = data
549
550         function datasource.get(self, section, option)
551                 return data[section] and data[section][option]
552         end
553
554         function datasource.del(...)
555                 return true
556         end
557
558         function datasource.get_scheme()
559                 return nil
560         end
561
562         AbstractSection.__init__(self, datasource, "table", ...)
563         self.template = "cbi/tblsection"
564         self.rowcolors = true
565         self.anonymous = true
566 end
567
568 function Table.parse(self)
569         for i, k in ipairs(self:cfgsections()) do
570                 if luci.http.formvalue("cbi.submit") then
571                         Node.parse(self, k)
572                 end
573         end
574 end
575
576 function Table.cfgsections(self)
577         local sections = {}
578
579         for i, v in luci.util.kspairs(self.data) do
580                 table.insert(sections, i)
581         end
582
583         return sections
584 end
585
586
587
588 --[[
589 NamedSection - A fixed configuration section defined by its name
590 ]]--
591 NamedSection = class(AbstractSection)
592
593 function NamedSection.__init__(self, map, section, stype, ...)
594         AbstractSection.__init__(self, map, stype, ...)
595         Node._i18n(self, map.config, section, nil, ...)
596
597         -- Defaults
598         self.addremove = false
599
600         -- Use defaults from UVL
601         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
602                 local vs = self.map:get_scheme(self.sectiontype)
603                 self.addremove = not vs.unique and not vs.required
604                 self.dynamic   = vs.dynamic
605                 self.title       = self.title or vs.title
606                 self.description = self.description or vs.descr
607         end
608
609         self.template = "cbi/nsection"
610         self.section = section
611 end
612
613 function NamedSection.parse(self)
614         local s = self.section
615         local active = self:cfgvalue(s)
616
617
618         if self.addremove then
619                 local path = self.config.."."..s
620                 if active then -- Remove the section
621                         if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
622                                 return
623                         end
624                 else           -- Create and apply default values
625                         if luci.http.formvalue("cbi.cns."..path) then
626                                 self:create(s)
627                                 return
628                         end
629                 end
630         end
631
632         if active then
633                 AbstractSection.parse_dynamic(self, s)
634                 if luci.http.formvalue("cbi.submit") then
635                         Node.parse(self, s)
636                 end
637                 AbstractSection.parse_optionals(self, s)
638         end
639 end
640
641
642 --[[
643 TypedSection - A (set of) configuration section(s) defined by the type
644         addremove:      Defines whether the user can add/remove sections of this type
645         anonymous:  Allow creating anonymous sections
646         validate:       a validation function returning nil if the section is invalid
647 ]]--
648 TypedSection = class(AbstractSection)
649
650 function TypedSection.__init__(self, map, type, ...)
651         AbstractSection.__init__(self, map, type, ...)
652         Node._i18n(self, map.config, type, nil, ...)
653
654         self.template  = "cbi/tsection"
655         self.deps = {}
656         self.anonymous = false
657
658         -- Use defaults from UVL
659         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
660                 local vs = self.map:get_scheme(self.sectiontype)
661                 self.addremove = not vs.unique and not vs.required
662                 self.dynamic   = vs.dynamic
663                 self.anonymous = not vs.named
664                 self.title       = self.title or vs.title
665                 self.description = self.description or vs.descr
666         end
667 end
668
669 -- Return all matching UCI sections for this TypedSection
670 function TypedSection.cfgsections(self)
671         local sections = {}
672         uci.foreach(self.map.config, self.sectiontype,
673                 function (section)
674                         if self:checkscope(section[".name"]) then
675                                 table.insert(sections, section[".name"])
676                         end
677                 end)
678
679         return sections
680 end
681
682 -- Limits scope to sections that have certain option => value pairs
683 function TypedSection.depends(self, option, value)
684         table.insert(self.deps, {option=option, value=value})
685 end
686
687 function TypedSection.parse(self)
688         if self.addremove then
689                 -- Create
690                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
691                 local name  = luci.http.formvalue(crval)
692                 if self.anonymous then
693                         if name then
694                                 self:create()
695                         end
696                 else
697                         if name then
698                                 -- Ignore if it already exists
699                                 if self:cfgvalue(name) then
700                                         name = nil;
701                                 end
702
703                                 name = self:checkscope(name)
704
705                                 if not name then
706                                         self.err_invalid = true
707                                 end
708
709                                 if name and name:len() > 0 then
710                                         self:create(name)
711                                 end
712                         end
713                 end
714
715                 -- Remove
716                 crval = REMOVE_PREFIX .. self.config
717                 name = luci.http.formvaluetable(crval)
718                 for k,v in pairs(name) do
719                         luci.util.perror(k)
720                         luci.util.perror(self:cfgvalue(k))
721                         luci.util.perror(self:checkscope(k))
722                         if self:cfgvalue(k) and self:checkscope(k) then
723                                 self:remove(k)
724                         end
725                 end
726         end
727
728         for i, k in ipairs(self:cfgsections()) do
729                 AbstractSection.parse_dynamic(self, k)
730                 if luci.http.formvalue("cbi.submit") then
731                         Node.parse(self, k)
732                 end
733                 AbstractSection.parse_optionals(self, k)
734         end
735 end
736
737 -- Verifies scope of sections
738 function TypedSection.checkscope(self, section)
739         -- Check if we are not excluded
740         if self.filter and not self:filter(section) then
741                 return nil
742         end
743
744         -- Check if at least one dependency is met
745         if #self.deps > 0 and self:cfgvalue(section) then
746                 local stat = false
747
748                 for k, v in ipairs(self.deps) do
749                         if self:cfgvalue(section)[v.option] == v.value then
750                                 stat = true
751                         end
752                 end
753
754                 if not stat then
755                         return nil
756                 end
757         end
758
759         return self:validate(section)
760 end
761
762
763 -- Dummy validate function
764 function TypedSection.validate(self, section)
765         return section
766 end
767
768
769 --[[
770 AbstractValue - An abstract Value Type
771         null:           Value can be empty
772         valid:          A function returning the value if it is valid otherwise nil
773         depends:        A table of option => value pairs of which one must be true
774         default:        The default value
775         size:           The size of the input fields
776         rmempty:        Unset value if empty
777         optional:       This value is optional (see AbstractSection.optionals)
778 ]]--
779 AbstractValue = class(Node)
780
781 function AbstractValue.__init__(self, map, section, option, ...)
782         Node.__init__(self, ...)
783         self.section = section
784         self.option  = option
785         self.map     = map
786         self.config  = map.config
787         self.tag_invalid = {}
788         self.tag_missing = {}
789         self.tag_error = {}
790         self.deps = {}
791         self.cast = "string"
792
793         self.track_missing = false
794         self.rmempty   = false
795         self.default   = nil
796         self.size      = nil
797         self.optional  = false
798
799         -- Use defaults from UVL
800         if not self.override_scheme
801          and self.map:get_scheme(self.section.sectiontype, self.option) then
802                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
803                 self.rmempty     = not vs.required
804                 self.cast        = (vs.type == "list") and "list" or "string"
805                 self.title       = self.title or vs.title
806                 self.description = self.description or vs.descr
807
808                 if vs.depends and not self.override_dependencies then
809                         for i, deps in ipairs(vs.depends) do
810                                 deps = _uvl_strip_remote_dependencies(deps)
811                                 if next(deps) then
812                                         self:depends(deps)
813                                 end
814                         end
815                 end
816         end
817 end
818
819 -- Add a dependencie to another section field
820 function AbstractValue.depends(self, field, value)
821         local deps
822         if type(field) == "string" then
823                 deps = {}
824                 deps[field] = value
825         else
826                 deps = field
827         end
828
829         table.insert(self.deps, {deps=deps, add=""})
830 end
831
832 -- Generates the unique CBID
833 function AbstractValue.cbid(self, section)
834         return "cbid."..self.map.config.."."..section.."."..self.option
835 end
836
837 -- Return whether this object should be created
838 function AbstractValue.formcreated(self, section)
839         local key = "cbi.opt."..self.config.."."..section
840         return (luci.http.formvalue(key) == self.option)
841 end
842
843 -- Returns the formvalue for this object
844 function AbstractValue.formvalue(self, section)
845         return luci.http.formvalue(self:cbid(section))
846 end
847
848 function AbstractValue.additional(self, value)
849         self.optional = value
850 end
851
852 function AbstractValue.mandatory(self, value)
853         self.rmempty = not value
854 end
855
856 function AbstractValue.parse(self, section)
857         local fvalue = self:formvalue(section)
858         local cvalue = self:cfgvalue(section)
859
860         if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
861                 fvalue = self:transform(self:validate(fvalue, section))
862                 if not fvalue then
863                         self.tag_invalid[section] = true
864                 end
865                 if fvalue and not (fvalue == cvalue) then
866                         self:write(section, fvalue)
867                 end
868         else                                                    -- Unset the UCI or error
869                 if self.rmempty or self.optional then
870                         self:remove(section)
871                 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
872                         self.tag_missing[section] = true
873                 end
874         end
875 end
876
877 -- Render if this value exists or if it is mandatory
878 function AbstractValue.render(self, s, scope)
879         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
880                 scope = scope or {}
881                 scope.section   = s
882                 scope.cbid      = self:cbid(s)
883                 scope.striptags = luci.util.striptags
884
885                 scope.ifattr = function(cond,key,val)
886                         if cond then
887                                 return string.format(
888                                         ' %s="%s"', tostring(key),
889                                         luci.util.pcdata(tostring( val
890                                          or scope[key]
891                                          or (type(self[key]) ~= "function" and self[key])
892                                          or "" ))
893                                 )
894                         else
895                                 return ''
896                         end
897                 end
898
899                 scope.attr = function(...)
900                         return scope.ifattr( true, ... )
901                 end
902
903                 Node.render(self, scope)
904         end
905 end
906
907 -- Return the UCI value of this object
908 function AbstractValue.cfgvalue(self, section)
909         local value = self.map:get(section, self.option)
910         if not self.cast or self.cast == type(value) then
911                 return value
912         elseif self.cast == "string" then
913                 if type(value) == "table" then
914                         return value[1]
915                 end
916         elseif self.cast == "table" then
917                 return {value}
918         end
919 end
920
921 -- Validate the form value
922 function AbstractValue.validate(self, value)
923         return value
924 end
925
926 AbstractValue.transform = AbstractValue.validate
927
928
929 -- Write to UCI
930 function AbstractValue.write(self, section, value)
931         return self.map:set(section, self.option, value)
932 end
933
934 -- Remove from UCI
935 function AbstractValue.remove(self, section)
936         return self.map:del(section, self.option)
937 end
938
939
940
941
942 --[[
943 Value - A one-line value
944         maxlength:      The maximum length
945 ]]--
946 Value = class(AbstractValue)
947
948 function Value.__init__(self, ...)
949         AbstractValue.__init__(self, ...)
950         self.template  = "cbi/value"
951         self.keylist = {}
952         self.vallist = {}
953 end
954
955 function Value.value(self, key, val)
956         val = val or key
957         table.insert(self.keylist, tostring(key))
958         table.insert(self.vallist, tostring(val))
959 end
960
961
962 -- DummyValue - This does nothing except being there
963 DummyValue = class(AbstractValue)
964
965 function DummyValue.__init__(self, ...)
966         AbstractValue.__init__(self, ...)
967         self.template = "cbi/dvalue"
968         self.value = nil
969 end
970
971 function DummyValue.parse(self)
972
973 end
974
975
976 --[[
977 Flag - A flag being enabled or disabled
978 ]]--
979 Flag = class(AbstractValue)
980
981 function Flag.__init__(self, ...)
982         AbstractValue.__init__(self, ...)
983         self.template  = "cbi/fvalue"
984
985         self.enabled = "1"
986         self.disabled = "0"
987 end
988
989 -- A flag can only have two states: set or unset
990 function Flag.parse(self, section)
991         local fvalue = self:formvalue(section)
992
993         if fvalue then
994                 fvalue = self.enabled
995         else
996                 fvalue = self.disabled
997         end
998
999         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1000                 if not(fvalue == self:cfgvalue(section)) then
1001                         self:write(section, fvalue)
1002                 end
1003         else
1004                 self:remove(section)
1005         end
1006 end
1007
1008
1009
1010 --[[
1011 ListValue - A one-line value predefined in a list
1012         widget: The widget that will be used (select, radio)
1013 ]]--
1014 ListValue = class(AbstractValue)
1015
1016 function ListValue.__init__(self, ...)
1017         AbstractValue.__init__(self, ...)
1018         self.template  = "cbi/lvalue"
1019
1020         self.keylist = {}
1021         self.vallist = {}
1022         self.size   = 1
1023         self.widget = "select"
1024
1025         if not self.override_scheme
1026          and self.map:get_scheme(self.section.sectiontype, self.option) then
1027                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1028                 if self.value and vs.values and not self.override_values then
1029                         if self.rmempty or self.optional then
1030                                 self:value("")
1031                         end
1032                         for k, v in pairs(vs.values) do
1033                                 local deps = {}
1034                                 if vs.enum_depends and vs.enum_depends[k] then
1035                                         deps = _uvl_strip_remote_dependencies(vs.enum_depends[k])
1036                                 end
1037                                 self:value(k, v, unpack(deps))
1038                         end
1039                 end
1040         end
1041 end
1042
1043 function ListValue.value(self, key, val, ...)
1044         val = val or key
1045         table.insert(self.keylist, tostring(key))
1046         table.insert(self.vallist, tostring(val))
1047
1048         for i, deps in ipairs({...}) do
1049                 table.insert(self.deps, {add = "-"..key, deps=deps})
1050         end
1051 end
1052
1053 function ListValue.validate(self, val)
1054         if luci.util.contains(self.keylist, val) then
1055                 return val
1056         else
1057                 return nil
1058         end
1059 end
1060
1061
1062
1063 --[[
1064 MultiValue - Multiple delimited values
1065         widget: The widget that will be used (select, checkbox)
1066         delimiter: The delimiter that will separate the values (default: " ")
1067 ]]--
1068 MultiValue = class(AbstractValue)
1069
1070 function MultiValue.__init__(self, ...)
1071         AbstractValue.__init__(self, ...)
1072         self.template = "cbi/mvalue"
1073
1074         self.keylist = {}
1075         self.vallist = {}
1076
1077         self.widget = "checkbox"
1078         self.delimiter = " "
1079 end
1080
1081 function MultiValue.render(self, ...)
1082         if self.widget == "select" and not self.size then
1083                 self.size = #self.vallist
1084         end
1085
1086         AbstractValue.render(self, ...)
1087 end
1088
1089 function MultiValue.value(self, key, val)
1090         val = val or key
1091         table.insert(self.keylist, tostring(key))
1092         table.insert(self.vallist, tostring(val))
1093 end
1094
1095 function MultiValue.valuelist(self, section)
1096         local val = self:cfgvalue(section)
1097
1098         if not(type(val) == "string") then
1099                 return {}
1100         end
1101
1102         return luci.util.split(val, self.delimiter)
1103 end
1104
1105 function MultiValue.validate(self, val)
1106         val = (type(val) == "table") and val or {val}
1107
1108         local result
1109
1110         for i, value in ipairs(val) do
1111                 if luci.util.contains(self.keylist, value) then
1112                         result = result and (result .. self.delimiter .. value) or value
1113                 end
1114         end
1115
1116         return result
1117 end
1118
1119
1120 StaticList = class(MultiValue)
1121
1122 function StaticList.__init__(self, ...)
1123         MultiValue.__init__(self, ...)
1124         self.cast = "table"
1125         self.valuelist = self.cfgvalue
1126
1127         if not self.override_scheme
1128          and self.map:get_scheme(self.section.sectiontype, self.option) then
1129                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1130                 if self.value and vs.values and not self.override_values then
1131                         for k, v in pairs(vs.values) do
1132                                 self:value(k, v)
1133                         end
1134                 end
1135         end
1136 end
1137
1138 function StaticList.validate(self, value)
1139         value = (type(value) == "table") and value or {value}
1140
1141         local valid = {}
1142         for i, v in ipairs(value) do
1143                 if luci.util.contains(self.valuelist, v) then
1144                         table.insert(valid, v)
1145                 end
1146         end
1147         return valid
1148 end
1149
1150
1151 DynamicList = class(AbstractValue)
1152
1153 function DynamicList.__init__(self, ...)
1154         AbstractValue.__init__(self, ...)
1155         self.template  = "cbi/dynlist"
1156         self.cast = "table"
1157         self.keylist = {}
1158         self.vallist = {}
1159 end
1160
1161 function DynamicList.value(self, key, val)
1162         val = val or key
1163         table.insert(self.keylist, tostring(key))
1164         table.insert(self.vallist, tostring(val))
1165 end
1166
1167 function DynamicList.validate(self, value, section)
1168         value = (type(value) == "table") and value or {value}
1169
1170         local valid = {}
1171         for i, v in ipairs(value) do
1172                 if v and #v > 0 and
1173                  not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1174                         table.insert(valid, v)
1175                 end
1176         end
1177
1178         return valid
1179 end
1180
1181
1182 --[[
1183 TextValue - A multi-line value
1184         rows:   Rows
1185 ]]--
1186 TextValue = class(AbstractValue)
1187
1188 function TextValue.__init__(self, ...)
1189         AbstractValue.__init__(self, ...)
1190         self.template  = "cbi/tvalue"
1191 end
1192
1193 --[[
1194 Button
1195 ]]--
1196 Button = class(AbstractValue)
1197
1198 function Button.__init__(self, ...)
1199         AbstractValue.__init__(self, ...)
1200         self.template  = "cbi/button"
1201         self.inputstyle = nil
1202         self.rmempty = true
1203 end