[PATCH] Naive fix for bug #208
[project/luci.git] / libs / web / 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 local util = require("luci.util")
31 require("luci.http")
32
33
34 --local event      = require "luci.sys.event"
35 local fs         = require("nixio.fs")
36 local uci        = require("luci.model.uci")
37 local datatypes  = require("luci.cbi.datatypes")
38 local class      = util.class
39 local instanceof = util.instanceof
40
41 FORM_NODATA  =  0
42 FORM_PROCEED =  0
43 FORM_VALID   =  1
44 FORM_DONE        =  1
45 FORM_INVALID = -1
46 FORM_CHANGED =  2
47 FORM_SKIP    =  4
48
49 AUTO = true
50
51 CREATE_PREFIX = "cbi.cts."
52 REMOVE_PREFIX = "cbi.rts."
53 RESORT_PREFIX = "cbi.sts."
54 FEXIST_PREFIX = "cbi.cbe."
55
56 -- Loads a CBI map from given file, creating an environment and returns it
57 function load(cbimap, ...)
58         local fs   = require "nixio.fs"
59         local i18n = require "luci.i18n"
60         require("luci.config")
61         require("luci.util")
62
63         local upldir = "/lib/uci/upload/"
64         local cbidir = luci.util.libpath() .. "/model/cbi/"
65         local func, err
66
67         if fs.access(cbidir..cbimap..".lua") then
68                 func, err = loadfile(cbidir..cbimap..".lua")
69         elseif fs.access(cbimap) then
70                 func, err = loadfile(cbimap)
71         else
72                 func, err = nil, "Model '" .. cbimap .. "' not found!"
73         end
74
75         assert(func, err)
76
77         luci.i18n.loadc("base")
78
79         local env = {
80                 translate=i18n.translate,
81                 translatef=i18n.translatef,
82                 arg={...}
83         }
84
85         setfenv(func, setmetatable(env, {__index =
86                 function(tbl, key)
87                         return rawget(tbl, key) or _M[key] or _G[key]
88                 end}))
89
90         local maps       = { func() }
91         local uploads    = { }
92         local has_upload = false
93
94         for i, map in ipairs(maps) do
95                 if not instanceof(map, Node) then
96                         error("CBI map returns no valid map object!")
97                         return nil
98                 else
99                         map:prepare()
100                         if map.upload_fields then
101                                 has_upload = true
102                                 for _, field in ipairs(map.upload_fields) do
103                                         uploads[
104                                                 field.config .. '.' ..
105                                                 (field.section.sectiontype or '1') .. '.' ..
106                                                 field.option
107                                         ] = true
108                                 end
109                         end
110                 end
111         end
112
113         if has_upload then
114                 local uci = luci.model.uci.cursor()
115                 local prm = luci.http.context.request.message.params
116                 local fd, cbid
117
118                 luci.http.setfilehandler(
119                         function( field, chunk, eof )
120                                 if not field then return end
121                                 if field.name and not cbid then
122                                         local c, s, o = field.name:gmatch(
123                                                 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
124                                         )()
125
126                                         if c and s and o then
127                                                 local t = uci:get( c, s ) or s
128                                                 if uploads[c.."."..t.."."..o] then
129                                                         local path = upldir .. field.name
130                                                         fd = io.open(path, "w")
131                                                         if fd then
132                                                                 cbid = field.name
133                                                                 prm[cbid] = path
134                                                         end
135                                                 end
136                                         end
137                                 end
138
139                                 if field.name == cbid and fd then
140                                         fd:write(chunk)
141                                 end
142
143                                 if eof and fd then
144                                         fd:close()
145                                         fd   = nil
146                                         cbid = nil
147                                 end
148                         end
149                 )
150         end
151
152         return maps
153 end
154
155
156 -- Node pseudo abstract class
157 Node = class()
158
159 function Node.__init__(self, title, description)
160         self.children = {}
161         self.title = title or ""
162         self.description = description or ""
163         self.template = "cbi/node"
164 end
165
166 -- hook helper
167 function Node._run_hook(self, hook)
168         if type(self[hook]) == "function" then
169                 return self[hook](self)
170         end
171 end
172
173 function Node._run_hooks(self, ...)
174         local f
175         local r = false
176         for _, f in ipairs(arg) do
177                 if type(self[f]) == "function" then
178                         self[f](self)
179                         r = true
180                 end
181         end
182         return r
183 end
184
185 -- Prepare nodes
186 function Node.prepare(self, ...)
187         for k, child in ipairs(self.children) do
188                 child:prepare(...)
189         end
190 end
191
192 -- Append child nodes
193 function Node.append(self, obj)
194         table.insert(self.children, obj)
195 end
196
197 -- Parse this node and its children
198 function Node.parse(self, ...)
199         for k, child in ipairs(self.children) do
200                 child:parse(...)
201         end
202 end
203
204 -- Render this node
205 function Node.render(self, scope)
206         scope = scope or {}
207         scope.self = self
208
209         luci.template.render(self.template, scope)
210 end
211
212 -- Render the children
213 function Node.render_children(self, ...)
214         local k, node
215         for k, node in ipairs(self.children) do
216                 node.last_child = (k == #self.children)
217                 node:render(...)
218         end
219 end
220
221
222 --[[
223 A simple template element
224 ]]--
225 Template = class(Node)
226
227 function Template.__init__(self, template)
228         Node.__init__(self)
229         self.template = template
230 end
231
232 function Template.render(self)
233         luci.template.render(self.template, {self=self})
234 end
235
236 function Template.parse(self, readinput)
237         self.readinput = (readinput ~= false)
238         return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
239 end
240
241
242 --[[
243 Map - A map describing a configuration file
244 ]]--
245 Map = class(Node)
246
247 function Map.__init__(self, config, ...)
248         Node.__init__(self, ...)
249
250         self.config = config
251         self.parsechain = {self.config}
252         self.template = "cbi/map"
253         self.apply_on_parse = nil
254         self.readinput = true
255         self.proceed = false
256         self.flow = {}
257
258         self.uci = uci.cursor()
259         self.save = true
260
261         self.changed = false
262
263         if not self.uci:load(self.config) then
264                 error("Unable to read UCI data: " .. self.config)
265         end
266 end
267
268 function Map.formvalue(self, key)
269         return self.readinput and luci.http.formvalue(key)
270 end
271
272 function Map.formvaluetable(self, key)
273         return self.readinput and luci.http.formvaluetable(key) or {}
274 end
275
276 function Map.get_scheme(self, sectiontype, option)
277         if not option then
278                 return self.scheme and self.scheme.sections[sectiontype]
279         else
280                 return self.scheme and self.scheme.variables[sectiontype]
281                  and self.scheme.variables[sectiontype][option]
282         end
283 end
284
285 function Map.submitstate(self)
286         return self:formvalue("cbi.submit")
287 end
288
289 -- Chain foreign config
290 function Map.chain(self, config)
291         table.insert(self.parsechain, config)
292 end
293
294 function Map.state_handler(self, state)
295         return state
296 end
297
298 -- Use optimized UCI writing
299 function Map.parse(self, readinput, ...)
300         self.readinput = (readinput ~= false)
301         self:_run_hooks("on_parse")
302
303         if self:formvalue("cbi.skip") then
304                 self.state = FORM_SKIP
305                 return self:state_handler(self.state)
306         end
307
308         Node.parse(self, ...)
309
310         if self.save then
311                 self:_run_hooks("on_save", "on_before_save")
312                 for i, config in ipairs(self.parsechain) do
313                         self.uci:save(config)
314                 end
315                 self:_run_hooks("on_after_save")
316                 if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
317                         self:_run_hooks("on_before_commit")
318                         for i, config in ipairs(self.parsechain) do
319                                 self.uci:commit(config)
320
321                                 -- Refresh data because commit changes section names
322                                 self.uci:load(config)
323                         end
324                         self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
325                         if self.apply_on_parse then
326                                 self.uci:apply(self.parsechain)
327                                 self:_run_hooks("on_apply", "on_after_apply")
328                         else
329                                 -- This is evaluated by the dispatcher and delegated to the
330                                 -- template which in turn fires XHR to perform the actual
331                                 -- apply actions.
332                                 self.apply_needed = true
333                         end
334
335                         -- Reparse sections
336                         Node.parse(self, true)
337
338                 end
339                 for i, config in ipairs(self.parsechain) do
340                         self.uci:unload(config)
341                 end
342                 if type(self.commit_handler) == "function" then
343                         self:commit_handler(self:submitstate())
344                 end
345         end
346
347         if self:submitstate() then
348                 if not self.save then
349                         self.state = FORM_INVALID
350                 elseif self.proceed then
351                         self.state = FORM_PROCEED
352                 else
353                         self.state = self.changed and FORM_CHANGED or FORM_VALID
354                 end
355         else
356                 self.state = FORM_NODATA
357         end
358
359         return self:state_handler(self.state)
360 end
361
362 function Map.render(self, ...)
363         self:_run_hooks("on_init")
364         Node.render(self, ...)
365 end
366
367 -- Creates a child section
368 function Map.section(self, class, ...)
369         if instanceof(class, AbstractSection) then
370                 local obj  = class(self, ...)
371                 self:append(obj)
372                 return obj
373         else
374                 error("class must be a descendent of AbstractSection")
375         end
376 end
377
378 -- UCI add
379 function Map.add(self, sectiontype)
380         return self.uci:add(self.config, sectiontype)
381 end
382
383 -- UCI set
384 function Map.set(self, section, option, value)
385         if type(value) ~= "table" or #value > 0 then
386                 if option then
387                         return self.uci:set(self.config, section, option, value)
388                 else
389                         return self.uci:set(self.config, section, value)
390                 end
391         else
392                 return Map.del(self, section, option)
393         end
394 end
395
396 -- UCI del
397 function Map.del(self, section, option)
398         if option then
399                 return self.uci:delete(self.config, section, option)
400         else
401                 return self.uci:delete(self.config, section)
402         end
403 end
404
405 -- UCI get
406 function Map.get(self, section, option)
407         if not section then
408                 return self.uci:get_all(self.config)
409         elseif option then
410                 return self.uci:get(self.config, section, option)
411         else
412                 return self.uci:get_all(self.config, section)
413         end
414 end
415
416 --[[
417 Compound - Container
418 ]]--
419 Compound = class(Node)
420
421 function Compound.__init__(self, ...)
422         Node.__init__(self)
423         self.template = "cbi/compound"
424         self.children = {...}
425 end
426
427 function Compound.populate_delegator(self, delegator)
428         for _, v in ipairs(self.children) do
429                 v.delegator = delegator
430         end
431 end
432
433 function Compound.parse(self, ...)
434         local cstate, state = 0
435
436         for k, child in ipairs(self.children) do
437                 cstate = child:parse(...)
438                 state = (not state or cstate < state) and cstate or state
439         end
440
441         return state
442 end
443
444
445 --[[
446 Delegator - Node controller
447 ]]--
448 Delegator = class(Node)
449 function Delegator.__init__(self, ...)
450         Node.__init__(self, ...)
451         self.nodes = {}
452         self.defaultpath = {}
453         self.pageaction = false
454         self.readinput = true
455         self.allow_reset = false
456         self.allow_cancel = false
457         self.allow_back = false
458         self.allow_finish = false
459         self.template = "cbi/delegator"
460 end
461
462 function Delegator.set(self, name, node)
463         assert(not self.nodes[name], "Duplicate entry")
464
465         self.nodes[name] = node
466 end
467
468 function Delegator.add(self, name, node)
469         node = self:set(name, node)
470         self.defaultpath[#self.defaultpath+1] = name
471 end
472
473 function Delegator.insert_after(self, name, after)
474         local n = #self.chain + 1
475         for k, v in ipairs(self.chain) do
476                 if v == after then
477                         n = k + 1
478                         break
479                 end
480         end
481         table.insert(self.chain, n, name)
482 end
483
484 function Delegator.set_route(self, ...)
485         local n, chain, route = 0, self.chain, {...}
486         for i = 1, #chain do
487                 if chain[i] == self.current then
488                         n = i
489                         break
490                 end
491         end
492         for i = 1, #route do
493                 n = n + 1
494                 chain[n] = route[i]
495         end
496         for i = n + 1, #chain do
497                 chain[i] = nil
498         end
499 end
500
501 function Delegator.get(self, name)
502         local node = self.nodes[name]
503
504         if type(node) == "string" then
505                 node = load(node, name)
506         end
507
508         if type(node) == "table" and getmetatable(node) == nil then
509                 node = Compound(unpack(node))
510         end
511
512         return node
513 end
514
515 function Delegator.parse(self, ...)
516         if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
517                 if self:_run_hooks("on_cancel") then
518                         return FORM_DONE
519                 end
520         end
521
522         if not Map.formvalue(self, "cbi.delg.current") then
523                 self:_run_hooks("on_init")
524         end
525
526         local newcurrent
527         self.chain = self.chain or self:get_chain()
528         self.current = self.current or self:get_active()
529         self.active = self.active or self:get(self.current)
530         assert(self.active, "Invalid state")
531
532         local stat = FORM_DONE
533         if type(self.active) ~= "function" then
534                 self.active:populate_delegator(self)
535                 stat = self.active:parse()
536         else
537                 self:active()
538         end
539
540         if stat > FORM_PROCEED then
541                 if Map.formvalue(self, "cbi.delg.back") then
542                         newcurrent = self:get_prev(self.current)
543                 else
544                         newcurrent = self:get_next(self.current)
545                 end
546         elseif stat < FORM_PROCEED then
547                 return stat
548         end
549
550
551         if not Map.formvalue(self, "cbi.submit") then
552                 return FORM_NODATA
553         elseif stat > FORM_PROCEED
554         and (not newcurrent or not self:get(newcurrent)) then
555                 return self:_run_hook("on_done") or FORM_DONE
556         else
557                 self.current = newcurrent or self.current
558                 self.active = self:get(self.current)
559                 if type(self.active) ~= "function" then
560                         self.active:populate_delegator(self)
561                         local stat = self.active:parse(false)
562                         if stat == FORM_SKIP then
563                                 return self:parse(...)
564                         else
565                                 return FORM_PROCEED
566                         end
567                 else
568                         return self:parse(...)
569                 end
570         end
571 end
572
573 function Delegator.get_next(self, state)
574         for k, v in ipairs(self.chain) do
575                 if v == state then
576                         return self.chain[k+1]
577                 end
578         end
579 end
580
581 function Delegator.get_prev(self, state)
582         for k, v in ipairs(self.chain) do
583                 if v == state then
584                         return self.chain[k-1]
585                 end
586         end
587 end
588
589 function Delegator.get_chain(self)
590         local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
591         return type(x) == "table" and x or {x}
592 end
593
594 function Delegator.get_active(self)
595         return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
596 end
597
598 --[[
599 Page - A simple node
600 ]]--
601
602 Page = class(Node)
603 Page.__init__ = Node.__init__
604 Page.parse    = function() end
605
606
607 --[[
608 SimpleForm - A Simple non-UCI form
609 ]]--
610 SimpleForm = class(Node)
611
612 function SimpleForm.__init__(self, config, title, description, data)
613         Node.__init__(self, title, description)
614         self.config = config
615         self.data = data or {}
616         self.template = "cbi/simpleform"
617         self.dorender = true
618         self.pageaction = false
619         self.readinput = true
620 end
621
622 SimpleForm.formvalue = Map.formvalue
623 SimpleForm.formvaluetable = Map.formvaluetable
624
625 function SimpleForm.parse(self, readinput, ...)
626         self.readinput = (readinput ~= false)
627
628         if self:formvalue("cbi.skip") then
629                 return FORM_SKIP
630         end
631
632         if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
633                 return FORM_DONE
634         end
635
636         if self:submitstate() then
637                 Node.parse(self, 1, ...)
638         end
639
640         local valid = true
641         for k, j in ipairs(self.children) do
642                 for i, v in ipairs(j.children) do
643                         valid = valid
644                          and (not v.tag_missing or not v.tag_missing[1])
645                          and (not v.tag_invalid or not v.tag_invalid[1])
646                          and (not v.error)
647                 end
648         end
649
650         local state =
651                 not self:submitstate() and FORM_NODATA
652                 or valid and FORM_VALID
653                 or FORM_INVALID
654
655         self.dorender = not self.handle
656         if self.handle then
657                 local nrender, nstate = self:handle(state, self.data)
658                 self.dorender = self.dorender or (nrender ~= false)
659                 state = nstate or state
660         end
661         return state
662 end
663
664 function SimpleForm.render(self, ...)
665         if self.dorender then
666                 Node.render(self, ...)
667         end
668 end
669
670 function SimpleForm.submitstate(self)
671         return self:formvalue("cbi.submit")
672 end
673
674 function SimpleForm.section(self, class, ...)
675         if instanceof(class, AbstractSection) then
676                 local obj  = class(self, ...)
677                 self:append(obj)
678                 return obj
679         else
680                 error("class must be a descendent of AbstractSection")
681         end
682 end
683
684 -- Creates a child field
685 function SimpleForm.field(self, class, ...)
686         local section
687         for k, v in ipairs(self.children) do
688                 if instanceof(v, SimpleSection) then
689                         section = v
690                         break
691                 end
692         end
693         if not section then
694                 section = self:section(SimpleSection)
695         end
696
697         if instanceof(class, AbstractValue) then
698                 local obj  = class(self, section, ...)
699                 obj.track_missing = true
700                 section:append(obj)
701                 return obj
702         else
703                 error("class must be a descendent of AbstractValue")
704         end
705 end
706
707 function SimpleForm.set(self, section, option, value)
708         self.data[option] = value
709 end
710
711
712 function SimpleForm.del(self, section, option)
713         self.data[option] = nil
714 end
715
716
717 function SimpleForm.get(self, section, option)
718         return self.data[option]
719 end
720
721
722 function SimpleForm.get_scheme()
723         return nil
724 end
725
726
727 Form = class(SimpleForm)
728
729 function Form.__init__(self, ...)
730         SimpleForm.__init__(self, ...)
731         self.embedded = true
732 end
733
734
735 --[[
736 AbstractSection
737 ]]--
738 AbstractSection = class(Node)
739
740 function AbstractSection.__init__(self, map, sectiontype, ...)
741         Node.__init__(self, ...)
742         self.sectiontype = sectiontype
743         self.map = map
744         self.config = map.config
745         self.optionals = {}
746         self.defaults = {}
747         self.fields = {}
748         self.tag_error = {}
749         self.tag_invalid = {}
750         self.tag_deperror = {}
751         self.changed = false
752
753         self.optional = true
754         self.addremove = false
755         self.dynamic = false
756 end
757
758 -- Define a tab for the section
759 function AbstractSection.tab(self, tab, title, desc)
760         self.tabs      = self.tabs      or { }
761         self.tab_names = self.tab_names or { }
762
763         self.tab_names[#self.tab_names+1] = tab
764         self.tabs[tab] = {
765                 title       = title,
766                 description = desc,
767                 childs      = { }
768         }
769 end
770
771 -- Check whether the section has tabs
772 function AbstractSection.has_tabs(self)
773         return (self.tabs ~= nil) and (next(self.tabs) ~= nil)
774 end
775
776 -- Appends a new option
777 function AbstractSection.option(self, class, option, ...)
778         if instanceof(class, AbstractValue) then
779                 local obj  = class(self.map, self, option, ...)
780                 self:append(obj)
781                 self.fields[option] = obj
782                 return obj
783         elseif class == true then
784                 error("No valid class was given and autodetection failed.")
785         else
786                 error("class must be a descendant of AbstractValue")
787         end
788 end
789
790 -- Appends a new tabbed option
791 function AbstractSection.taboption(self, tab, ...)
792
793         assert(tab and self.tabs and self.tabs[tab],
794                 "Cannot assign option to not existing tab %q" % tostring(tab))
795
796         local l = self.tabs[tab].childs
797         local o = AbstractSection.option(self, ...)
798
799         if o then l[#l+1] = o end
800
801         return o
802 end
803
804 -- Render a single tab
805 function AbstractSection.render_tab(self, tab, ...)
806
807         assert(tab and self.tabs and self.tabs[tab],
808                 "Cannot render not existing tab %q" % tostring(tab))
809
810         local k, node
811         for k, node in ipairs(self.tabs[tab].childs) do
812                 node.last_child = (k == #self.tabs[tab].childs)
813                 node:render(...)
814         end
815 end
816
817 -- Parse optional options
818 function AbstractSection.parse_optionals(self, section)
819         if not self.optional then
820                 return
821         end
822
823         self.optionals[section] = {}
824
825         local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
826         for k,v in ipairs(self.children) do
827                 if v.optional and not v:cfgvalue(section) and not self:has_tabs() then
828                         if field == v.option then
829                                 field = nil
830                                 self.map.proceed = true
831                         else
832                                 table.insert(self.optionals[section], v)
833                         end
834                 end
835         end
836
837         if field and #field > 0 and self.dynamic then
838                 self:add_dynamic(field)
839         end
840 end
841
842 -- Add a dynamic option
843 function AbstractSection.add_dynamic(self, field, optional)
844         local o = self:option(Value, field, field)
845         o.optional = optional
846 end
847
848 -- Parse all dynamic options
849 function AbstractSection.parse_dynamic(self, section)
850         if not self.dynamic then
851                 return
852         end
853
854         local arr  = luci.util.clone(self:cfgvalue(section))
855         local form = self.map:formvaluetable("cbid."..self.config.."."..section)
856         for k, v in pairs(form) do
857                 arr[k] = v
858         end
859
860         for key,val in pairs(arr) do
861                 local create = true
862
863                 for i,c in ipairs(self.children) do
864                         if c.option == key then
865                                 create = false
866                         end
867                 end
868
869                 if create and key:sub(1, 1) ~= "." then
870                         self.map.proceed = true
871                         self:add_dynamic(key, true)
872                 end
873         end
874 end
875
876 -- Returns the section's UCI table
877 function AbstractSection.cfgvalue(self, section)
878         return self.map:get(section)
879 end
880
881 -- Push events
882 function AbstractSection.push_events(self)
883         --luci.util.append(self.map.events, self.events)
884         self.map.changed = true
885 end
886
887 -- Removes the section
888 function AbstractSection.remove(self, section)
889         self.map.proceed = true
890         return self.map:del(section)
891 end
892
893 -- Creates the section
894 function AbstractSection.create(self, section)
895         local stat
896
897         if section then
898                 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
899         else
900                 section = self.map:add(self.sectiontype)
901                 stat = section
902         end
903
904         if stat then
905                 for k,v in pairs(self.children) do
906                         if v.default then
907                                 self.map:set(section, v.option, v.default)
908                         end
909                 end
910
911                 for k,v in pairs(self.defaults) do
912                         self.map:set(section, k, v)
913                 end
914         end
915
916         self.map.proceed = true
917
918         return stat
919 end
920
921
922 SimpleSection = class(AbstractSection)
923
924 function SimpleSection.__init__(self, form, ...)
925         AbstractSection.__init__(self, form, nil, ...)
926         self.template = "cbi/nullsection"
927 end
928
929
930 Table = class(AbstractSection)
931
932 function Table.__init__(self, form, data, ...)
933         local datasource = {}
934         local tself = self
935         datasource.config = "table"
936         self.data = data or {}
937
938         datasource.formvalue = Map.formvalue
939         datasource.formvaluetable = Map.formvaluetable
940         datasource.readinput = true
941
942         function datasource.get(self, section, option)
943                 return tself.data[section] and tself.data[section][option]
944         end
945
946         function datasource.submitstate(self)
947                 return Map.formvalue(self, "cbi.submit")
948         end
949
950         function datasource.del(...)
951                 return true
952         end
953
954         function datasource.get_scheme()
955                 return nil
956         end
957
958         AbstractSection.__init__(self, datasource, "table", ...)
959         self.template = "cbi/tblsection"
960         self.rowcolors = true
961         self.anonymous = true
962 end
963
964 function Table.parse(self, readinput)
965         self.map.readinput = (readinput ~= false)
966         for i, k in ipairs(self:cfgsections()) do
967                 if self.map:submitstate() then
968                         Node.parse(self, k)
969                 end
970         end
971 end
972
973 function Table.cfgsections(self)
974         local sections = {}
975
976         for i, v in luci.util.kspairs(self.data) do
977                 table.insert(sections, i)
978         end
979
980         return sections
981 end
982
983 function Table.update(self, data)
984         self.data = data
985 end
986
987
988
989 --[[
990 NamedSection - A fixed configuration section defined by its name
991 ]]--
992 NamedSection = class(AbstractSection)
993
994 function NamedSection.__init__(self, map, section, stype, ...)
995         AbstractSection.__init__(self, map, stype, ...)
996
997         -- Defaults
998         self.addremove = false
999         self.template = "cbi/nsection"
1000         self.section = section
1001 end
1002
1003 function NamedSection.parse(self, novld)
1004         local s = self.section
1005         local active = self:cfgvalue(s)
1006
1007         if self.addremove then
1008                 local path = self.config.."."..s
1009                 if active then -- Remove the section
1010                         if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1011                                 self:push_events()
1012                                 return
1013                         end
1014                 else           -- Create and apply default values
1015                         if self.map:formvalue("cbi.cns."..path) then
1016                                 self:create(s)
1017                                 return
1018                         end
1019                 end
1020         end
1021
1022         if active then
1023                 AbstractSection.parse_dynamic(self, s)
1024                 if self.map:submitstate() then
1025                         Node.parse(self, s)
1026                 end
1027                 AbstractSection.parse_optionals(self, s)
1028
1029                 if self.changed then
1030                         self:push_events()
1031                 end
1032         end
1033 end
1034
1035
1036 --[[
1037 TypedSection - A (set of) configuration section(s) defined by the type
1038         addremove:      Defines whether the user can add/remove sections of this type
1039         anonymous:  Allow creating anonymous sections
1040         validate:       a validation function returning nil if the section is invalid
1041 ]]--
1042 TypedSection = class(AbstractSection)
1043
1044 function TypedSection.__init__(self, map, type, ...)
1045         AbstractSection.__init__(self, map, type, ...)
1046
1047         self.template = "cbi/tsection"
1048         self.deps = {}
1049         self.anonymous = false
1050 end
1051
1052 -- Return all matching UCI sections for this TypedSection
1053 function TypedSection.cfgsections(self)
1054         local sections = {}
1055         self.map.uci:foreach(self.map.config, self.sectiontype,
1056                 function (section)
1057                         if self:checkscope(section[".name"]) then
1058                                 table.insert(sections, section[".name"])
1059                         end
1060                 end)
1061
1062         return sections
1063 end
1064
1065 -- Limits scope to sections that have certain option => value pairs
1066 function TypedSection.depends(self, option, value)
1067         table.insert(self.deps, {option=option, value=value})
1068 end
1069
1070 function TypedSection.parse(self, novld)
1071         if self.addremove then
1072                 -- Remove
1073                 local crval = REMOVE_PREFIX .. self.config
1074                 local name = self.map:formvaluetable(crval)
1075                 for k,v in pairs(name) do
1076                         if k:sub(-2) == ".x" then
1077                                 k = k:sub(1, #k - 2)
1078                         end
1079                         if self:cfgvalue(k) and self:checkscope(k) then
1080                                 self:remove(k)
1081                         end
1082                 end
1083         end
1084
1085         local co
1086         for i, k in ipairs(self:cfgsections()) do
1087                 AbstractSection.parse_dynamic(self, k)
1088                 if self.map:submitstate() then
1089                         Node.parse(self, k, novld)
1090                 end
1091                 AbstractSection.parse_optionals(self, k)
1092         end
1093
1094         if self.addremove then
1095                 -- Create
1096                 local created
1097                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1098                 local origin, name = next(self.map:formvaluetable(crval))
1099                 if self.anonymous then
1100                         if name then
1101                                 created = self:create(nil, origin)
1102                         end
1103                 else
1104                         if name then
1105                                 -- Ignore if it already exists
1106                                 if self:cfgvalue(name) then
1107                                         name = nil;
1108                                 end
1109
1110                                 name = self:checkscope(name)
1111
1112                                 if not name then
1113                                         self.err_invalid = true
1114                                 end
1115
1116                                 if name and #name > 0 then
1117                                         created = self:create(name, origin) and name
1118                                         if not created then
1119                                                 self.invalid_cts = true
1120                                         end
1121                                 end
1122                         end
1123                 end
1124
1125                 if created then
1126                         AbstractSection.parse_optionals(self, created)
1127                 end
1128         end
1129
1130         if self.sortable then
1131                 local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype
1132                 local order = self.map:formvalue(stval)
1133                 if order and #order > 0 then
1134                         local sid
1135                         local num = 0
1136                         for sid in util.imatch(order) do
1137                                 self.map.uci:reorder(self.config, sid, num)
1138                                 num = num + 1
1139                         end
1140                         self.changed = (num > 0)
1141                 end
1142         end
1143
1144         if created or self.changed then
1145                 self:push_events()
1146         end
1147 end
1148
1149 -- Verifies scope of sections
1150 function TypedSection.checkscope(self, section)
1151         -- Check if we are not excluded
1152         if self.filter and not self:filter(section) then
1153                 return nil
1154         end
1155
1156         -- Check if at least one dependency is met
1157         if #self.deps > 0 and self:cfgvalue(section) then
1158                 local stat = false
1159
1160                 for k, v in ipairs(self.deps) do
1161                         if self:cfgvalue(section)[v.option] == v.value then
1162                                 stat = true
1163                         end
1164                 end
1165
1166                 if not stat then
1167                         return nil
1168                 end
1169         end
1170
1171         return self:validate(section)
1172 end
1173
1174
1175 -- Dummy validate function
1176 function TypedSection.validate(self, section)
1177         return section
1178 end
1179
1180
1181 --[[
1182 AbstractValue - An abstract Value Type
1183         null:           Value can be empty
1184         valid:          A function returning the value if it is valid otherwise nil
1185         depends:        A table of option => value pairs of which one must be true
1186         default:        The default value
1187         size:           The size of the input fields
1188         rmempty:        Unset value if empty
1189         optional:       This value is optional (see AbstractSection.optionals)
1190 ]]--
1191 AbstractValue = class(Node)
1192
1193 function AbstractValue.__init__(self, map, section, option, ...)
1194         Node.__init__(self, ...)
1195         self.section = section
1196         self.option  = option
1197         self.map     = map
1198         self.config  = map.config
1199         self.tag_invalid = {}
1200         self.tag_missing = {}
1201         self.tag_reqerror = {}
1202         self.tag_error = {}
1203         self.deps = {}
1204         self.subdeps = {}
1205         --self.cast = "string"
1206
1207         self.track_missing = false
1208         self.rmempty   = true
1209         self.default   = nil
1210         self.size      = nil
1211         self.optional  = false
1212 end
1213
1214 function AbstractValue.prepare(self)
1215         self.cast = self.cast or "string"
1216 end
1217
1218 -- Add a dependencie to another section field
1219 function AbstractValue.depends(self, field, value)
1220         local deps
1221         if type(field) == "string" then
1222                 deps = {}
1223                 deps[field] = value
1224         else
1225                 deps = field
1226         end
1227
1228         table.insert(self.deps, {deps=deps, add=""})
1229 end
1230
1231 -- Generates the unique CBID
1232 function AbstractValue.cbid(self, section)
1233         return "cbid."..self.map.config.."."..section.."."..self.option
1234 end
1235
1236 -- Return whether this object should be created
1237 function AbstractValue.formcreated(self, section)
1238         local key = "cbi.opt."..self.config.."."..section
1239         return (self.map:formvalue(key) == self.option)
1240 end
1241
1242 -- Returns the formvalue for this object
1243 function AbstractValue.formvalue(self, section)
1244         return self.map:formvalue(self:cbid(section))
1245 end
1246
1247 function AbstractValue.additional(self, value)
1248         self.optional = value
1249 end
1250
1251 function AbstractValue.mandatory(self, value)
1252         self.rmempty = not value
1253 end
1254
1255 function AbstractValue.add_error(self, section, type, msg)
1256         self.error = self.error or { }
1257         self.error[section] = msg or type
1258
1259         self.section.error = self.section.error or { }
1260         self.section.error[section] = self.section.error[section] or { }
1261         table.insert(self.section.error[section], msg or type)
1262
1263         if type == "invalid" then
1264                 self.tag_invalid[section] = true
1265         elseif type == "missing" then
1266                 self.tag_missing[section] = true
1267         end
1268
1269         self.tag_error[section] = true
1270         self.map.save = false
1271 end
1272
1273 function AbstractValue.parse(self, section, novld)
1274         local fvalue = self:formvalue(section)
1275         local cvalue = self:cfgvalue(section)
1276
1277         -- If favlue and cvalue are both tables and have the same content
1278         -- make them identical
1279         if type(fvalue) == "table" and type(cvalue) == "table" then
1280                 local equal = #fvalue == #cvalue
1281                 if equal then
1282                         for i=1, #fvalue do
1283                                 if cvalue[i] ~= fvalue[i] then
1284                                         equal = false
1285                                 end
1286                         end
1287                 end
1288                 if equal then
1289                         fvalue = cvalue
1290                 end
1291         end
1292
1293         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1294                 local val_err
1295                 fvalue, val_err = self:validate(fvalue, section)
1296                 fvalue = self:transform(fvalue)
1297
1298                 if not fvalue and not novld then
1299                         self:add_error(section, "invalid", val_err)
1300                 end
1301
1302                 if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
1303                         if self:write(section, fvalue) then
1304                                 -- Push events
1305                                 self.section.changed = true
1306                                 --luci.util.append(self.map.events, self.events)
1307                         end
1308                 end
1309         else                                                    -- Unset the UCI or error
1310                 if self.rmempty or self.optional then
1311                         if self:remove(section) then
1312                                 -- Push events
1313                                 self.section.changed = true
1314                                 --luci.util.append(self.map.events, self.events)
1315                         end
1316                 elseif cvalue ~= fvalue and not novld then
1317                         -- trigger validator with nil value to get custom user error msg.
1318                         local _, val_err = self:validate(nil, section)
1319                         self:add_error(section, "missing", val_err)
1320                 end
1321         end
1322 end
1323
1324 -- Render if this value exists or if it is mandatory
1325 function AbstractValue.render(self, s, scope)
1326         if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
1327                 scope = scope or {}
1328                 scope.section = s
1329                 scope.cbid    = self:cbid(s)
1330                 Node.render(self, scope)
1331         end
1332 end
1333
1334 -- Return the UCI value of this object
1335 function AbstractValue.cfgvalue(self, section)
1336         local value
1337         if self.tag_error[section] then
1338                 value = self:formvalue(section)
1339         else
1340                 value = self.map:get(section, self.option)
1341         end
1342
1343         if not value then
1344                 return nil
1345         elseif not self.cast or self.cast == type(value) then
1346                 return value
1347         elseif self.cast == "string" then
1348                 if type(value) == "table" then
1349                         return value[1]
1350                 end
1351         elseif self.cast == "table" then
1352                 return { value }
1353         end
1354 end
1355
1356 -- Validate the form value
1357 function AbstractValue.validate(self, value)
1358         if self.datatype and value then
1359                 local args = { }
1360                 local dt, ar = self.datatype:match("^(%w+)%(([^%(%)]+)%)")
1361
1362                 if dt and ar then
1363                         local a
1364                         for a in ar:gmatch("[^%s,]+") do
1365                                 args[#args+1] = a
1366                         end
1367                 else
1368                         dt = self.datatype
1369                 end
1370
1371                 if dt and datatypes[dt] then
1372                         if type(value) == "table" then
1373                                 local v
1374                                 for _, v in ipairs(value) do
1375                                         if v and #v > 0 and not datatypes[dt](v, unpack(args)) then
1376                                                 return nil
1377                                         end
1378                                 end
1379                         else
1380                                 if not datatypes[dt](value, unpack(args)) then
1381                                         return nil
1382                                 end
1383                         end
1384                 end
1385         end
1386
1387         return value
1388 end
1389
1390 AbstractValue.transform = AbstractValue.validate
1391
1392
1393 -- Write to UCI
1394 function AbstractValue.write(self, section, value)
1395         return self.map:set(section, self.option, value)
1396 end
1397
1398 -- Remove from UCI
1399 function AbstractValue.remove(self, section)
1400         return self.map:del(section, self.option)
1401 end
1402
1403
1404
1405
1406 --[[
1407 Value - A one-line value
1408         maxlength:      The maximum length
1409 ]]--
1410 Value = class(AbstractValue)
1411
1412 function Value.__init__(self, ...)
1413         AbstractValue.__init__(self, ...)
1414         self.template  = "cbi/value"
1415         self.keylist = {}
1416         self.vallist = {}
1417 end
1418
1419 function Value.reset_values(self)
1420         self.keylist = {}
1421         self.vallist = {}
1422 end
1423
1424 function Value.value(self, key, val)
1425         val = val or key
1426         table.insert(self.keylist, tostring(key))
1427         table.insert(self.vallist, tostring(val))
1428 end
1429
1430
1431 -- DummyValue - This does nothing except being there
1432 DummyValue = class(AbstractValue)
1433
1434 function DummyValue.__init__(self, ...)
1435         AbstractValue.__init__(self, ...)
1436         self.template = "cbi/dvalue"
1437         self.value = nil
1438 end
1439
1440 function DummyValue.cfgvalue(self, section)
1441         local value
1442         if self.value then
1443                 if type(self.value) == "function" then
1444                         value = self:value(section)
1445                 else
1446                         value = self.value
1447                 end
1448         else
1449                 value = AbstractValue.cfgvalue(self, section)
1450         end
1451         return value
1452 end
1453
1454 function DummyValue.parse(self)
1455
1456 end
1457
1458
1459 --[[
1460 Flag - A flag being enabled or disabled
1461 ]]--
1462 Flag = class(AbstractValue)
1463
1464 function Flag.__init__(self, ...)
1465         AbstractValue.__init__(self, ...)
1466         self.template  = "cbi/fvalue"
1467
1468         self.enabled  = "1"
1469         self.disabled = "0"
1470         self.default  = self.disabled
1471 end
1472
1473 -- A flag can only have two states: set or unset
1474 function Flag.parse(self, section)
1475         local fexists = self.map:formvalue(
1476                 FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
1477
1478         if fexists then
1479                 local fvalue = self:formvalue(section) and self.enabled or self.disabled
1480                 if fvalue ~= self.default or (not self.optional and not self.rmempty) then
1481                         self:write(section, fvalue)
1482                 else
1483                         self:remove(section)
1484                 end
1485         else
1486                 self:remove(section)
1487         end
1488 end
1489
1490 function Flag.cfgvalue(self, section)
1491         return AbstractValue.cfgvalue(self, section) or self.default
1492 end
1493
1494
1495 --[[
1496 ListValue - A one-line value predefined in a list
1497         widget: The widget that will be used (select, radio)
1498 ]]--
1499 ListValue = class(AbstractValue)
1500
1501 function ListValue.__init__(self, ...)
1502         AbstractValue.__init__(self, ...)
1503         self.template  = "cbi/lvalue"
1504
1505         self.keylist = {}
1506         self.vallist = {}
1507         self.size   = 1
1508         self.widget = "select"
1509 end
1510
1511 function ListValue.reset_values(self)
1512         self.keylist = {}
1513         self.vallist = {}
1514 end
1515
1516 function ListValue.value(self, key, val, ...)
1517         if luci.util.contains(self.keylist, key) then
1518                 return
1519         end
1520
1521         val = val or key
1522         table.insert(self.keylist, tostring(key))
1523         table.insert(self.vallist, tostring(val))
1524
1525         for i, deps in ipairs({...}) do
1526                 self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
1527         end
1528 end
1529
1530 function ListValue.validate(self, val)
1531         if luci.util.contains(self.keylist, val) then
1532                 return val
1533         else
1534                 return nil
1535         end
1536 end
1537
1538
1539
1540 --[[
1541 MultiValue - Multiple delimited values
1542         widget: The widget that will be used (select, checkbox)
1543         delimiter: The delimiter that will separate the values (default: " ")
1544 ]]--
1545 MultiValue = class(AbstractValue)
1546
1547 function MultiValue.__init__(self, ...)
1548         AbstractValue.__init__(self, ...)
1549         self.template = "cbi/mvalue"
1550
1551         self.keylist = {}
1552         self.vallist = {}
1553
1554         self.widget = "checkbox"
1555         self.delimiter = " "
1556 end
1557
1558 function MultiValue.render(self, ...)
1559         if self.widget == "select" and not self.size then
1560                 self.size = #self.vallist
1561         end
1562
1563         AbstractValue.render(self, ...)
1564 end
1565
1566 function MultiValue.reset_values(self)
1567         self.keylist = {}
1568         self.vallist = {}
1569 end
1570
1571 function MultiValue.value(self, key, val)
1572         if luci.util.contains(self.keylist, key) then
1573                 return
1574         end
1575
1576         val = val or key
1577         table.insert(self.keylist, tostring(key))
1578         table.insert(self.vallist, tostring(val))
1579 end
1580
1581 function MultiValue.valuelist(self, section)
1582         local val = self:cfgvalue(section)
1583
1584         if not(type(val) == "string") then
1585                 return {}
1586         end
1587
1588         return luci.util.split(val, self.delimiter)
1589 end
1590
1591 function MultiValue.validate(self, val)
1592         val = (type(val) == "table") and val or {val}
1593
1594         local result
1595
1596         for i, value in ipairs(val) do
1597                 if luci.util.contains(self.keylist, value) then
1598                         result = result and (result .. self.delimiter .. value) or value
1599                 end
1600         end
1601
1602         return result
1603 end
1604
1605
1606 StaticList = class(MultiValue)
1607
1608 function StaticList.__init__(self, ...)
1609         MultiValue.__init__(self, ...)
1610         self.cast = "table"
1611         self.valuelist = self.cfgvalue
1612
1613         if not self.override_scheme
1614          and self.map:get_scheme(self.section.sectiontype, self.option) then
1615                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1616                 if self.value and vs.values and not self.override_values then
1617                         for k, v in pairs(vs.values) do
1618                                 self:value(k, v)
1619                         end
1620                 end
1621         end
1622 end
1623
1624 function StaticList.validate(self, value)
1625         value = (type(value) == "table") and value or {value}
1626
1627         local valid = {}
1628         for i, v in ipairs(value) do
1629                 if luci.util.contains(self.keylist, v) then
1630                         table.insert(valid, v)
1631                 end
1632         end
1633         return valid
1634 end
1635
1636
1637 DynamicList = class(AbstractValue)
1638
1639 function DynamicList.__init__(self, ...)
1640         AbstractValue.__init__(self, ...)
1641         self.template  = "cbi/dynlist"
1642         self.cast = "table"
1643         self.keylist = {}
1644         self.vallist = {}
1645 end
1646
1647 function DynamicList.reset_values(self)
1648         self.keylist = {}
1649         self.vallist = {}
1650 end
1651
1652 function DynamicList.value(self, key, val)
1653         val = val or key
1654         table.insert(self.keylist, tostring(key))
1655         table.insert(self.vallist, tostring(val))
1656 end
1657
1658 function DynamicList.write(self, section, value)
1659         local t = { }
1660
1661         if type(value) == "table" then
1662                 local x
1663                 for _, x in ipairs(value) do
1664                         if x and #x > 0 then
1665                                 t[#t+1] = x
1666                         end
1667                 end
1668         else
1669                 t = { value }
1670         end
1671
1672         if self.cast == "string" then
1673                 value = table.concat(t, " ")
1674         else
1675                 value = t
1676         end
1677
1678         return AbstractValue.write(self, section, value)
1679 end
1680
1681 function DynamicList.cfgvalue(self, section)
1682         local value = AbstractValue.cfgvalue(self, section)
1683
1684         if type(value) == "string" then
1685                 local x
1686                 local t = { }
1687                 for x in value:gmatch("%S+") do
1688                         if #x > 0 then
1689                                 t[#t+1] = x
1690                         end
1691                 end
1692                 value = t
1693         end
1694
1695         return value
1696 end
1697
1698 function DynamicList.formvalue(self, section)
1699         local value = AbstractValue.formvalue(self, section)
1700
1701         if type(value) == "string" then
1702                 if self.cast == "string" then
1703                         local x
1704                         local t = { }
1705                         for x in value:gmatch("%S+") do
1706                                 t[#t+1] = x
1707                         end
1708                         value = t
1709                 else
1710                         value = { value }
1711                 end
1712         end
1713
1714         return value
1715 end
1716
1717
1718 --[[
1719 TextValue - A multi-line value
1720         rows:   Rows
1721 ]]--
1722 TextValue = class(AbstractValue)
1723
1724 function TextValue.__init__(self, ...)
1725         AbstractValue.__init__(self, ...)
1726         self.template  = "cbi/tvalue"
1727 end
1728
1729 --[[
1730 Button
1731 ]]--
1732 Button = class(AbstractValue)
1733
1734 function Button.__init__(self, ...)
1735         AbstractValue.__init__(self, ...)
1736         self.template  = "cbi/button"
1737         self.inputstyle = nil
1738         self.rmempty = true
1739 end
1740
1741
1742 FileUpload = class(AbstractValue)
1743
1744 function FileUpload.__init__(self, ...)
1745         AbstractValue.__init__(self, ...)
1746         self.template = "cbi/upload"
1747         if not self.map.upload_fields then
1748                 self.map.upload_fields = { self }
1749         else
1750                 self.map.upload_fields[#self.map.upload_fields+1] = self
1751         end
1752 end
1753
1754 function FileUpload.formcreated(self, section)
1755         return AbstractValue.formcreated(self, section) or
1756                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1757                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1758 end
1759
1760 function FileUpload.cfgvalue(self, section)
1761         local val = AbstractValue.cfgvalue(self, section)
1762         if val and fs.access(val) then
1763                 return val
1764         end
1765         return nil
1766 end
1767
1768 function FileUpload.formvalue(self, section)
1769         local val = AbstractValue.formvalue(self, section)
1770         if val then
1771                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1772                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1773                 then
1774                         return val
1775                 end
1776                 fs.unlink(val)
1777                 self.value = nil
1778         end
1779         return nil
1780 end
1781
1782 function FileUpload.remove(self, section)
1783         local val = AbstractValue.formvalue(self, section)
1784         if val and fs.access(val) then fs.unlink(val) end
1785         return AbstractValue.remove(self, section)
1786 end
1787
1788
1789 FileBrowser = class(AbstractValue)
1790
1791 function FileBrowser.__init__(self, ...)
1792         AbstractValue.__init__(self, ...)
1793         self.template = "cbi/browser"
1794 end