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