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