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