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