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