db76e3b92f338e6b564d0c597ba69b7323605546
[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         elseif stat < FORM_PROCEED then
582                 return stat
583         end
584         
585
586         if not Map.formvalue(self, "cbi.submit") then
587                 return FORM_NODATA
588         elseif not newcurrent or not self:get(newcurrent) then
589                 return FORM_DONE
590         else
591                 self.current = newcurrent
592                 self.active = self:get(self.current)
593                 if type(self.active) ~= "function" then
594                         self.active:parse(false)
595                         return FROM_PROCEED
596                 else
597                         return self:parse(...)
598                 end
599         end
600 end
601
602 function Delegator.get_next(self, state)
603         for k, v in ipairs(self.chain) do
604                 if v == state then
605                         return self.chain[k+1]
606                 end
607         end
608 end
609
610 function Delegator.get_prev(self, state)
611         for k, v in ipairs(self.chain) do
612                 if v == state then
613                         return self.chain[k-1]
614                 end
615         end
616 end
617
618 function Delegator.get_chain(self)
619         local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
620         return type(x) == "table" and x or {x}
621 end
622
623 function Delegator.get_active(self)
624         return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
625 end
626
627 --[[
628 Page - A simple node
629 ]]--
630
631 Page = class(Node)
632 Page.__init__ = Node.__init__
633 Page.parse    = function() end
634
635
636 --[[
637 SimpleForm - A Simple non-UCI form
638 ]]--
639 SimpleForm = class(Node)
640
641 function SimpleForm.__init__(self, config, title, description, data)
642         Node.__init__(self, title, description)
643         self.config = config
644         self.data = data or {}
645         self.template = "cbi/simpleform"
646         self.dorender = true
647         self.pageaction = false
648         self.readinput = true
649 end
650
651 SimpleForm.formvalue = Map.formvalue
652 SimpleForm.formvaluetable = Map.formvaluetable
653
654 function SimpleForm.parse(self, readinput, ...)
655         self.readinput = (readinput ~= false)
656
657         if self:formvalue("cbi.skip") then
658                 return FORM_SKIP
659         end
660
661         if self:submitstate() then
662                 Node.parse(self, 1, ...)
663         end
664
665         local valid = true
666         for k, j in ipairs(self.children) do
667                 for i, v in ipairs(j.children) do
668                         valid = valid
669                          and (not v.tag_missing or not v.tag_missing[1])
670                          and (not v.tag_invalid or not v.tag_invalid[1])
671                          and (not v.error)
672                 end
673         end
674
675         local state =
676                 not self:submitstate() and FORM_NODATA
677                 or valid and FORM_VALID
678                 or FORM_INVALID
679
680         self.dorender = not self.handle
681         if self.handle then
682                 local nrender, nstate = self:handle(state, self.data)
683                 self.dorender = self.dorender or (nrender ~= false)
684                 state = nstate or state
685         end
686         return state
687 end
688
689 function SimpleForm.render(self, ...)
690         if self.dorender then
691                 Node.render(self, ...)
692         end
693 end
694
695 function SimpleForm.submitstate(self)
696         return self:formvalue("cbi.submit")
697 end
698
699 function SimpleForm.section(self, class, ...)
700         if instanceof(class, AbstractSection) then
701                 local obj  = class(self, ...)
702                 self:append(obj)
703                 return obj
704         else
705                 error("class must be a descendent of AbstractSection")
706         end
707 end
708
709 -- Creates a child field
710 function SimpleForm.field(self, class, ...)
711         local section
712         for k, v in ipairs(self.children) do
713                 if instanceof(v, SimpleSection) then
714                         section = v
715                         break
716                 end
717         end
718         if not section then
719                 section = self:section(SimpleSection)
720         end
721
722         if instanceof(class, AbstractValue) then
723                 local obj  = class(self, section, ...)
724                 obj.track_missing = true
725                 section:append(obj)
726                 return obj
727         else
728                 error("class must be a descendent of AbstractValue")
729         end
730 end
731
732 function SimpleForm.set(self, section, option, value)
733         self.data[option] = value
734 end
735
736
737 function SimpleForm.del(self, section, option)
738         self.data[option] = nil
739 end
740
741
742 function SimpleForm.get(self, section, option)
743         return self.data[option]
744 end
745
746
747 function SimpleForm.get_scheme()
748         return nil
749 end
750
751
752 Form = class(SimpleForm)
753
754 function Form.__init__(self, ...)
755         SimpleForm.__init__(self, ...)
756         self.embedded = true
757 end
758
759
760 --[[
761 AbstractSection
762 ]]--
763 AbstractSection = class(Node)
764
765 function AbstractSection.__init__(self, map, sectiontype, ...)
766         Node.__init__(self, ...)
767         self.sectiontype = sectiontype
768         self.map = map
769         self.config = map.config
770         self.optionals = {}
771         self.defaults = {}
772         self.fields = {}
773         self.tag_error = {}
774         self.tag_invalid = {}
775         self.tag_deperror = {}
776         self.changed = false
777
778         self.optional = true
779         self.addremove = false
780         self.dynamic = false
781 end
782
783 -- Appends a new option
784 function AbstractSection.option(self, class, option, ...)
785         -- Autodetect from UVL
786         if class == true and self.map:get_scheme(self.sectiontype, option) then
787                 local vs = self.map:get_scheme(self.sectiontype, option)
788                 if vs.type == "boolean" then
789                         class = Flag
790                 elseif vs.type == "list" then
791                         class = DynamicList
792                 elseif vs.type == "enum" or vs.type == "reference" then
793                         class = ListValue
794                 else
795                         class = Value
796                 end
797         end
798
799         if instanceof(class, AbstractValue) then
800                 local obj  = class(self.map, self, option, ...)
801
802                 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
803
804                 self:append(obj)
805                 self.fields[option] = obj
806                 return obj
807         elseif class == true then
808                 error("No valid class was given and autodetection failed.")
809         else
810                 error("class must be a descendant of AbstractValue")
811         end
812 end
813
814 -- Parse optional options
815 function AbstractSection.parse_optionals(self, section)
816         if not self.optional then
817                 return
818         end
819
820         self.optionals[section] = {}
821
822         local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
823         for k,v in ipairs(self.children) do
824                 if v.optional and not v:cfgvalue(section) then
825                         if field == v.option then
826                                 field = nil
827                                 self.map.proceed = true
828                         else
829                                 table.insert(self.optionals[section], v)
830                         end
831                 end
832         end
833
834         if field and #field > 0 and self.dynamic then
835                 self:add_dynamic(field)
836         end
837 end
838
839 -- Add a dynamic option
840 function AbstractSection.add_dynamic(self, field, optional)
841         local o = self:option(Value, field, field)
842         o.optional = optional
843 end
844
845 -- Parse all dynamic options
846 function AbstractSection.parse_dynamic(self, section)
847         if not self.dynamic then
848                 return
849         end
850
851         local arr  = luci.util.clone(self:cfgvalue(section))
852         local form = self.map:formvaluetable("cbid."..self.config.."."..section)
853         for k, v in pairs(form) do
854                 arr[k] = v
855         end
856
857         for key,val in pairs(arr) do
858                 local create = true
859
860                 for i,c in ipairs(self.children) do
861                         if c.option == key then
862                                 create = false
863                         end
864                 end
865
866                 if create and key:sub(1, 1) ~= "." then
867                         self.map.proceed = true
868                         self:add_dynamic(key, true)
869                 end
870         end
871 end
872
873 -- Returns the section's UCI table
874 function AbstractSection.cfgvalue(self, section)
875         return self.map:get(section)
876 end
877
878 -- Push events
879 function AbstractSection.push_events(self)
880         --luci.util.append(self.map.events, self.events)
881         self.map.changed = true
882 end
883
884 -- Removes the section
885 function AbstractSection.remove(self, section)
886         self.map.proceed = true
887         return self.map:del(section)
888 end
889
890 -- Creates the section
891 function AbstractSection.create(self, section)
892         local stat
893
894         if section then
895                 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
896         else
897                 section = self.map:add(self.sectiontype)
898                 stat = section
899         end
900
901         if stat then
902                 for k,v in pairs(self.children) do
903                         if v.default then
904                                 self.map:set(section, v.option, v.default)
905                         end
906                 end
907
908                 for k,v in pairs(self.defaults) do
909                         self.map:set(section, k, v)
910                 end
911         end
912
913         self.map.proceed = true
914
915         return stat
916 end
917
918
919 SimpleSection = class(AbstractSection)
920
921 function SimpleSection.__init__(self, form, ...)
922         AbstractSection.__init__(self, form, nil, ...)
923         self.template = "cbi/nullsection"
924 end
925
926
927 Table = class(AbstractSection)
928
929 function Table.__init__(self, form, data, ...)
930         local datasource = {}
931         local tself = self
932         datasource.config = "table"
933         self.data = data or {}
934
935         datasource.formvalue = Map.formvalue
936         datasource.formvaluetable = Map.formvaluetable
937         datasource.readinput = true
938
939         function datasource.get(self, section, option)
940                 return tself.data[section] and tself.data[section][option]
941         end
942
943         function datasource.submitstate(self)
944                 return Map.formvalue(self, "cbi.submit")
945         end
946
947         function datasource.del(...)
948                 return true
949         end
950
951         function datasource.get_scheme()
952                 return nil
953         end
954
955         AbstractSection.__init__(self, datasource, "table", ...)
956         self.template = "cbi/tblsection"
957         self.rowcolors = true
958         self.anonymous = true
959 end
960
961 function Table.parse(self, readinput)
962         self.map.readinput = (readinput ~= false)
963         for i, k in ipairs(self:cfgsections()) do
964                 if self.map:submitstate() then
965                         Node.parse(self, k)
966                 end
967         end
968 end
969
970 function Table.cfgsections(self)
971         local sections = {}
972
973         for i, v in luci.util.kspairs(self.data) do
974                 table.insert(sections, i)
975         end
976
977         return sections
978 end
979
980 function Table.update(self, data)
981         self.data = data
982 end
983
984
985
986 --[[
987 NamedSection - A fixed configuration section defined by its name
988 ]]--
989 NamedSection = class(AbstractSection)
990
991 function NamedSection.__init__(self, map, section, stype, ...)
992         AbstractSection.__init__(self, map, stype, ...)
993         Node._i18n(self, map.config, section, nil, ...)
994
995         -- Defaults
996         self.addremove = false
997
998         -- Use defaults from UVL
999         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1000                 local vs = self.map:get_scheme(self.sectiontype)
1001                 self.addremove = not vs.unique and not vs.required
1002                 self.dynamic   = vs.dynamic
1003                 self.title       = self.title or vs.title
1004                 self.description = self.description or vs.descr
1005         end
1006
1007         self.template = "cbi/nsection"
1008         self.section = section
1009 end
1010
1011 function NamedSection.parse(self, novld)
1012         local s = self.section
1013         local active = self:cfgvalue(s)
1014
1015         if self.addremove then
1016                 local path = self.config.."."..s
1017                 if active then -- Remove the section
1018                         if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1019                                 self:push_events()
1020                                 return
1021                         end
1022                 else           -- Create and apply default values
1023                         if self.map:formvalue("cbi.cns."..path) then
1024                                 self:create(s)
1025                                 return
1026                         end
1027                 end
1028         end
1029
1030         if active then
1031                 AbstractSection.parse_dynamic(self, s)
1032                 if self.map:submitstate() then
1033                         Node.parse(self, s)
1034
1035                         if not novld and not self.override_scheme and self.map.scheme then
1036                                 _uvl_validate_section(self, s)
1037                         end
1038                 end
1039                 AbstractSection.parse_optionals(self, s)
1040
1041                 if self.changed then
1042                         self:push_events()
1043                 end
1044         end
1045 end
1046
1047
1048 --[[
1049 TypedSection - A (set of) configuration section(s) defined by the type
1050         addremove:      Defines whether the user can add/remove sections of this type
1051         anonymous:  Allow creating anonymous sections
1052         validate:       a validation function returning nil if the section is invalid
1053 ]]--
1054 TypedSection = class(AbstractSection)
1055
1056 function TypedSection.__init__(self, map, type, ...)
1057         AbstractSection.__init__(self, map, type, ...)
1058         Node._i18n(self, map.config, type, nil, ...)
1059
1060         self.template  = "cbi/tsection"
1061         self.deps = {}
1062         self.anonymous = false
1063
1064         -- Use defaults from UVL
1065         if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1066                 local vs = self.map:get_scheme(self.sectiontype)
1067                 self.addremove = not vs.unique and not vs.required
1068                 self.dynamic   = vs.dynamic
1069                 self.anonymous = not vs.named
1070                 self.title       = self.title or vs.title
1071                 self.description = self.description or vs.descr
1072         end
1073 end
1074
1075 -- Return all matching UCI sections for this TypedSection
1076 function TypedSection.cfgsections(self)
1077         local sections = {}
1078         self.map.uci:foreach(self.map.config, self.sectiontype,
1079                 function (section)
1080                         if self:checkscope(section[".name"]) then
1081                                 table.insert(sections, section[".name"])
1082                         end
1083                 end)
1084
1085         return sections
1086 end
1087
1088 -- Limits scope to sections that have certain option => value pairs
1089 function TypedSection.depends(self, option, value)
1090         table.insert(self.deps, {option=option, value=value})
1091 end
1092
1093 function TypedSection.parse(self, novld)
1094         if self.addremove then
1095                 -- Remove
1096                 local crval = REMOVE_PREFIX .. self.config
1097                 local name = self.map:formvaluetable(crval)
1098                 for k,v in pairs(name) do
1099                         if k:sub(-2) == ".x" then
1100                                 k = k:sub(1, #k - 2)
1101                         end
1102                         if self:cfgvalue(k) and self:checkscope(k) then
1103                                 self:remove(k)
1104                         end
1105                 end
1106         end
1107
1108         local co
1109         for i, k in ipairs(self:cfgsections()) do
1110                 AbstractSection.parse_dynamic(self, k)
1111                 if self.map:submitstate() then
1112                         Node.parse(self, k, novld)
1113
1114                         if not novld and not self.override_scheme and self.map.scheme then
1115                                 _uvl_validate_section(self, k)
1116                         end
1117                 end
1118                 AbstractSection.parse_optionals(self, k)
1119         end
1120
1121         if self.addremove then
1122                 -- Create
1123                 local created
1124                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1125                 local name  = self.map:formvalue(crval)
1126                 if self.anonymous then
1127                         if name then
1128                                 created = self:create()
1129                         end
1130                 else
1131                         if name then
1132                                 -- Ignore if it already exists
1133                                 if self:cfgvalue(name) then
1134                                         name = nil;
1135                                 end
1136
1137                                 name = self:checkscope(name)
1138
1139                                 if not name then
1140                                         self.err_invalid = true
1141                                 end
1142
1143                                 if name and #name > 0 then
1144                                         created = self:create(name) and name
1145                                         if not created then
1146                                                 self.invalid_cts = true
1147                                         end
1148                                 end
1149                         end
1150                 end
1151
1152                 if created then
1153                         AbstractSection.parse_optionals(self, created)
1154                 end
1155         end
1156
1157         if created or self.changed then
1158                 self:push_events()
1159         end
1160 end
1161
1162 -- Verifies scope of sections
1163 function TypedSection.checkscope(self, section)
1164         -- Check if we are not excluded
1165         if self.filter and not self:filter(section) then
1166                 return nil
1167         end
1168
1169         -- Check if at least one dependency is met
1170         if #self.deps > 0 and self:cfgvalue(section) then
1171                 local stat = false
1172
1173                 for k, v in ipairs(self.deps) do
1174                         if self:cfgvalue(section)[v.option] == v.value then
1175                                 stat = true
1176                         end
1177                 end
1178
1179                 if not stat then
1180                         return nil
1181                 end
1182         end
1183
1184         return self:validate(section)
1185 end
1186
1187
1188 -- Dummy validate function
1189 function TypedSection.validate(self, section)
1190         return section
1191 end
1192
1193
1194 --[[
1195 AbstractValue - An abstract Value Type
1196         null:           Value can be empty
1197         valid:          A function returning the value if it is valid otherwise nil
1198         depends:        A table of option => value pairs of which one must be true
1199         default:        The default value
1200         size:           The size of the input fields
1201         rmempty:        Unset value if empty
1202         optional:       This value is optional (see AbstractSection.optionals)
1203 ]]--
1204 AbstractValue = class(Node)
1205
1206 function AbstractValue.__init__(self, map, section, option, ...)
1207         Node.__init__(self, ...)
1208         self.section = section
1209         self.option  = option
1210         self.map     = map
1211         self.config  = map.config
1212         self.tag_invalid = {}
1213         self.tag_missing = {}
1214         self.tag_reqerror = {}
1215         self.tag_error = {}
1216         self.deps = {}
1217         --self.cast = "string"
1218
1219         self.track_missing = false
1220         self.rmempty   = true
1221         self.default   = nil
1222         self.size      = nil
1223         self.optional  = false
1224 end
1225
1226 function AbstractValue.prepare(self)
1227         -- Use defaults from UVL
1228         if not self.override_scheme
1229          and self.map:get_scheme(self.section.sectiontype, self.option) then
1230                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1231                 if self.cast == nil then
1232                         self.cast = (vs.type == "list") and "list" or "string"
1233                 end
1234                 self.title       = self.title or vs.title
1235                 self.description = self.description or vs.descr
1236                 if self.default == nil then
1237                         self.default = vs.default
1238                 end
1239
1240                 if vs.depends and not self.override_dependencies then
1241                         for i, deps in ipairs(vs.depends) do
1242                                 deps = _uvl_strip_remote_dependencies(deps)
1243                                 if next(deps) then
1244                                         self:depends(deps)
1245                                 end
1246                         end
1247                 end
1248         end
1249
1250         self.cast = self.cast or "string"
1251 end
1252
1253 -- Add a dependencie to another section field
1254 function AbstractValue.depends(self, field, value)
1255         local deps
1256         if type(field) == "string" then
1257                 deps = {}
1258                 deps[field] = value
1259         else
1260                 deps = field
1261         end
1262
1263         table.insert(self.deps, {deps=deps, add=""})
1264 end
1265
1266 -- Generates the unique CBID
1267 function AbstractValue.cbid(self, section)
1268         return "cbid."..self.map.config.."."..section.."."..self.option
1269 end
1270
1271 -- Return whether this object should be created
1272 function AbstractValue.formcreated(self, section)
1273         local key = "cbi.opt."..self.config.."."..section
1274         return (self.map:formvalue(key) == self.option)
1275 end
1276
1277 -- Returns the formvalue for this object
1278 function AbstractValue.formvalue(self, section)
1279         return self.map:formvalue(self:cbid(section))
1280 end
1281
1282 function AbstractValue.additional(self, value)
1283         self.optional = value
1284 end
1285
1286 function AbstractValue.mandatory(self, value)
1287         self.rmempty = not value
1288 end
1289
1290 function AbstractValue.parse(self, section, novld)
1291         local fvalue = self:formvalue(section)
1292         local cvalue = self:cfgvalue(section)
1293
1294         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1295                 fvalue = self:transform(self:validate(fvalue, section))
1296                 if not fvalue and not novld then
1297                         if self.error then
1298                                 self.error[section] = "invalid"
1299                         else
1300                                 self.error = { [section] = "invalid" }
1301                         end
1302                         if self.section.error then
1303                                 table.insert(self.section.error[section], "invalid")
1304                         else
1305                                 self.section.error = {[section] = {"invalid"}}
1306                         end 
1307                         self.map.save = false
1308                 end
1309                 if fvalue and not (fvalue == cvalue) then
1310                         if self:write(section, fvalue) then
1311                                 -- Push events
1312                                 self.section.changed = true
1313                                 --luci.util.append(self.map.events, self.events)
1314                         end
1315                 end
1316         else                                                    -- Unset the UCI or error
1317                 if self.rmempty or self.optional then
1318                         if self:remove(section) then
1319                                 -- Push events
1320                                 self.section.changed = true
1321                                 --luci.util.append(self.map.events, self.events)
1322                         end
1323                 elseif cvalue ~= fvalue and not novld then
1324                         self:write(section, fvalue or "")
1325                         if self.error then
1326                                 self.error[section] = "missing"
1327                         else
1328                                 self.error = { [section] = "missing" }
1329                         end
1330                         self.map.save = false
1331                 end
1332         end
1333 end
1334
1335 -- Render if this value exists or if it is mandatory
1336 function AbstractValue.render(self, s, scope)
1337         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1338                 scope = scope or {}
1339                 scope.section   = s
1340                 scope.cbid      = self:cbid(s)
1341                 scope.striptags = luci.util.striptags
1342
1343                 scope.ifattr = function(cond,key,val)
1344                         if cond then
1345                                 return string.format(
1346                                         ' %s="%s"', tostring(key),
1347                                         luci.util.pcdata(tostring( val
1348                                          or scope[key]
1349                                          or (type(self[key]) ~= "function" and self[key])
1350                                          or "" ))
1351                                 )
1352                         else
1353                                 return ''
1354                         end
1355                 end
1356
1357                 scope.attr = function(...)
1358                         return scope.ifattr( true, ... )
1359                 end
1360
1361                 Node.render(self, scope)
1362         end
1363 end
1364
1365 -- Return the UCI value of this object
1366 function AbstractValue.cfgvalue(self, section)
1367         local value = self.map:get(section, self.option)
1368         if not value then
1369                 return nil
1370         elseif not self.cast or self.cast == type(value) then
1371                 return value
1372         elseif self.cast == "string" then
1373                 if type(value) == "table" then
1374                         return value[1]
1375                 end
1376         elseif self.cast == "table" then
1377                 return luci.util.split(value, "%s+", nil, true)
1378         end
1379 end
1380
1381 -- Validate the form value
1382 function AbstractValue.validate(self, value)
1383         return value
1384 end
1385
1386 AbstractValue.transform = AbstractValue.validate
1387
1388
1389 -- Write to UCI
1390 function AbstractValue.write(self, section, value)
1391         return self.map:set(section, self.option, value)
1392 end
1393
1394 -- Remove from UCI
1395 function AbstractValue.remove(self, section)
1396         return self.map:del(section, self.option)
1397 end
1398
1399
1400
1401
1402 --[[
1403 Value - A one-line value
1404         maxlength:      The maximum length
1405 ]]--
1406 Value = class(AbstractValue)
1407
1408 function Value.__init__(self, ...)
1409         AbstractValue.__init__(self, ...)
1410         self.template  = "cbi/value"
1411         self.keylist = {}
1412         self.vallist = {}
1413 end
1414
1415 function Value.value(self, key, val)
1416         val = val or key
1417         table.insert(self.keylist, tostring(key))
1418         table.insert(self.vallist, tostring(val))
1419 end
1420
1421
1422 -- DummyValue - This does nothing except being there
1423 DummyValue = class(AbstractValue)
1424
1425 function DummyValue.__init__(self, ...)
1426         AbstractValue.__init__(self, ...)
1427         self.template = "cbi/dvalue"
1428         self.value = nil
1429 end
1430
1431 function DummyValue.cfgvalue(self, section)
1432         local value
1433         if self.value then
1434                 if type(self.value) == "function" then
1435                         value = self:value(section)
1436                 else
1437                         value = self.value
1438                 end
1439         else
1440                 value = AbstractValue.cfgvalue(self, section)
1441         end
1442         return value
1443 end
1444
1445 function DummyValue.parse(self)
1446
1447 end
1448
1449
1450 --[[
1451 Flag - A flag being enabled or disabled
1452 ]]--
1453 Flag = class(AbstractValue)
1454
1455 function Flag.__init__(self, ...)
1456         AbstractValue.__init__(self, ...)
1457         self.template  = "cbi/fvalue"
1458
1459         self.enabled = "1"
1460         self.disabled = "0"
1461 end
1462
1463 -- A flag can only have two states: set or unset
1464 function Flag.parse(self, section)
1465         local fvalue = self:formvalue(section)
1466
1467         if fvalue then
1468                 fvalue = self.enabled
1469         else
1470                 fvalue = self.disabled
1471         end
1472
1473         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1474                 if not(fvalue == self:cfgvalue(section)) then
1475                         self:write(section, fvalue)
1476                 end
1477         else
1478                 self:remove(section)
1479         end
1480 end
1481
1482
1483
1484 --[[
1485 ListValue - A one-line value predefined in a list
1486         widget: The widget that will be used (select, radio)
1487 ]]--
1488 ListValue = class(AbstractValue)
1489
1490 function ListValue.__init__(self, ...)
1491         AbstractValue.__init__(self, ...)
1492         self.template  = "cbi/lvalue"
1493
1494         self.keylist = {}
1495         self.vallist = {}
1496         self.size   = 1
1497         self.widget = "select"
1498 end
1499
1500 function ListValue.prepare(self, ...)
1501         AbstractValue.prepare(self, ...)
1502         if not self.override_scheme
1503          and self.map:get_scheme(self.section.sectiontype, self.option) then
1504                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1505                 if self.value and vs.valuelist and not self.override_values then
1506                         for k, v in ipairs(vs.valuelist) do
1507                                 local deps = {}
1508                                 if not self.override_dependencies
1509                                  and vs.enum_depends and vs.enum_depends[v.value] then
1510                                         for i, dep in ipairs(vs.enum_depends[v.value]) do
1511                                                 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1512                                         end
1513                                 end
1514                                 self:value(v.value, v.title or v.value, unpack(deps))
1515                         end
1516                 end
1517         end
1518 end
1519
1520 function ListValue.value(self, key, val, ...)
1521         if luci.util.contains(self.keylist, key) then
1522                 return
1523         end
1524
1525         val = val or key
1526         table.insert(self.keylist, tostring(key))
1527         table.insert(self.vallist, tostring(val))
1528
1529         for i, deps in ipairs({...}) do
1530                 table.insert(self.deps, {add = "-"..key, deps=deps})
1531         end
1532 end
1533
1534 function ListValue.validate(self, val)
1535         if luci.util.contains(self.keylist, val) then
1536                 return val
1537         else
1538                 return nil
1539         end
1540 end
1541
1542
1543
1544 --[[
1545 MultiValue - Multiple delimited values
1546         widget: The widget that will be used (select, checkbox)
1547         delimiter: The delimiter that will separate the values (default: " ")
1548 ]]--
1549 MultiValue = class(AbstractValue)
1550
1551 function MultiValue.__init__(self, ...)
1552         AbstractValue.__init__(self, ...)
1553         self.template = "cbi/mvalue"
1554
1555         self.keylist = {}
1556         self.vallist = {}
1557
1558         self.widget = "checkbox"
1559         self.delimiter = " "
1560 end
1561
1562 function MultiValue.render(self, ...)
1563         if self.widget == "select" and not self.size then
1564                 self.size = #self.vallist
1565         end
1566
1567         AbstractValue.render(self, ...)
1568 end
1569
1570 function MultiValue.value(self, key, val)
1571         if luci.util.contains(self.keylist, key) then
1572                 return
1573         end
1574
1575         val = val or key
1576         table.insert(self.keylist, tostring(key))
1577         table.insert(self.vallist, tostring(val))
1578 end
1579
1580 function MultiValue.valuelist(self, section)
1581         local val = self:cfgvalue(section)
1582
1583         if not(type(val) == "string") then
1584                 return {}
1585         end
1586
1587         return luci.util.split(val, self.delimiter)
1588 end
1589
1590 function MultiValue.validate(self, val)
1591         val = (type(val) == "table") and val or {val}
1592
1593         local result
1594
1595         for i, value in ipairs(val) do
1596                 if luci.util.contains(self.keylist, value) then
1597                         result = result and (result .. self.delimiter .. value) or value
1598                 end
1599         end
1600
1601         return result
1602 end
1603
1604
1605 StaticList = class(MultiValue)
1606
1607 function StaticList.__init__(self, ...)
1608         MultiValue.__init__(self, ...)
1609         self.cast = "table"
1610         self.valuelist = self.cfgvalue
1611
1612         if not self.override_scheme
1613          and self.map:get_scheme(self.section.sectiontype, self.option) then
1614                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1615                 if self.value and vs.values and not self.override_values then
1616                         for k, v in pairs(vs.values) do
1617                                 self:value(k, v)
1618                         end
1619                 end
1620         end
1621 end
1622
1623 function StaticList.validate(self, value)
1624         value = (type(value) == "table") and value or {value}
1625
1626         local valid = {}
1627         for i, v in ipairs(value) do
1628                 if luci.util.contains(self.keylist, v) then
1629                         table.insert(valid, v)
1630                 end
1631         end
1632         return valid
1633 end
1634
1635
1636 DynamicList = class(AbstractValue)
1637
1638 function DynamicList.__init__(self, ...)
1639         AbstractValue.__init__(self, ...)
1640         self.template  = "cbi/dynlist"
1641         self.cast = "table"
1642         self.keylist = {}
1643         self.vallist = {}
1644 end
1645
1646 function DynamicList.value(self, key, val)
1647         val = val or key
1648         table.insert(self.keylist, tostring(key))
1649         table.insert(self.vallist, tostring(val))
1650 end
1651
1652 function DynamicList.write(self, ...)
1653         self.map.proceed = true
1654         return AbstractValue.write(self, ...)
1655 end
1656
1657 function DynamicList.formvalue(self, section)
1658         local value = AbstractValue.formvalue(self, section)
1659         value = (type(value) == "table") and value or {value}
1660
1661         local valid = {}
1662         for i, v in ipairs(value) do
1663                 if v and #v > 0
1664                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1665                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1666                         table.insert(valid, v)
1667                 end
1668         end
1669
1670         return valid
1671 end
1672
1673
1674 --[[
1675 TextValue - A multi-line value
1676         rows:   Rows
1677 ]]--
1678 TextValue = class(AbstractValue)
1679
1680 function TextValue.__init__(self, ...)
1681         AbstractValue.__init__(self, ...)
1682         self.template  = "cbi/tvalue"
1683 end
1684
1685 --[[
1686 Button
1687 ]]--
1688 Button = class(AbstractValue)
1689
1690 function Button.__init__(self, ...)
1691         AbstractValue.__init__(self, ...)
1692         self.template  = "cbi/button"
1693         self.inputstyle = nil
1694         self.rmempty = true
1695 end
1696
1697
1698 FileUpload = class(AbstractValue)
1699
1700 function FileUpload.__init__(self, ...)
1701         AbstractValue.__init__(self, ...)
1702         self.template = "cbi/upload"
1703         if not self.map.upload_fields then
1704                 self.map.upload_fields = { self }
1705         else
1706                 self.map.upload_fields[#self.map.upload_fields+1] = self
1707         end
1708 end
1709
1710 function FileUpload.formcreated(self, section)
1711         return AbstractValue.formcreated(self, section) or
1712                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1713                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1714 end
1715
1716 function FileUpload.cfgvalue(self, section)
1717         local val = AbstractValue.cfgvalue(self, section)
1718         if val and fs.access(val) then
1719                 return val
1720         end
1721         return nil
1722 end
1723
1724 function FileUpload.formvalue(self, section)
1725         local val = AbstractValue.formvalue(self, section)
1726         if val then
1727                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1728                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1729                 then
1730                         return val
1731                 end
1732                 fs.unlink(val)
1733                 self.value = nil
1734         end
1735         return nil
1736 end
1737
1738 function FileUpload.remove(self, section)
1739         local val = AbstractValue.formvalue(self, section)
1740         if val and fs.access(val) then fs.unlink(val) end
1741         return AbstractValue.remove(self, section)
1742 end
1743
1744
1745 FileBrowser = class(AbstractValue)
1746
1747 function FileBrowser.__init__(self, ...)
1748         AbstractValue.__init__(self, ...)
1749         self.template = "cbi/browser"
1750 end