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