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