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