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