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