libs/cbi: Some fixes
[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 require("luci.util")
31 require("luci.http")
32 require("luci.model.uci")
33
34 local uci        = luci.model.uci
35 local class      = luci.util.class
36 local instanceof = luci.util.instanceof
37
38 FORM_NODATA  =  0
39 FORM_VALID   =  1
40 FORM_INVALID = -1
41
42 CREATE_PREFIX = "cbi.cts."
43 REMOVE_PREFIX = "cbi.rts."
44
45 -- Loads a CBI map from given file, creating an environment and returns it
46 function load(cbimap, ...)
47         require("luci.fs")
48         require("luci.i18n")
49         require("luci.config")
50         require("luci.util")
51
52         local cbidir = luci.util.libpath() .. "/model/cbi/"
53         local func, err = loadfile(cbidir..cbimap..".lua")
54
55         if not func then
56                 return nil
57         end
58
59         luci.i18n.loadc("cbi")
60
61         luci.util.resfenv(func)
62         luci.util.updfenv(func, luci.cbi)
63         luci.util.extfenv(func, "translate", luci.i18n.translate)
64         luci.util.extfenv(func, "translatef", luci.i18n.translatef)
65         luci.util.extfenv(func, "arg", {...})
66
67         local maps = {func()}
68
69         for i, map in ipairs(maps) do
70                 if not instanceof(map, Node) then
71                         error("CBI map returns no valid map object!")
72                         return nil
73                 end
74         end
75
76         return maps
77 end
78
79 -- Node pseudo abstract class
80 Node = class()
81
82 function Node.__init__(self, title, description)
83         self.children = {}
84         self.title = title or ""
85         self.description = description or ""
86         self.template = "cbi/node"
87 end
88
89 -- i18n helper
90 function Node._i18n(self, config, section, option, title, description)
91
92         -- i18n loaded?
93         if type(luci.i18n) == "table" then
94
95                 local key = config:gsub("[^%w]+", "")
96
97                 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
98                 if option  then key = key .. "_" .. option:lower():gsub("[^%w]+", "")  end
99
100                 self.title = title or luci.i18n.translate( key, option or section or config )
101                 self.description = description or luci.i18n.translate( key .. "_desc", "" )
102         end
103 end
104
105 -- Append child nodes
106 function Node.append(self, obj)
107         table.insert(self.children, obj)
108 end
109
110 -- Parse this node and its children
111 function Node.parse(self, ...)
112         for k, child in ipairs(self.children) do
113                 child:parse(...)
114         end
115 end
116
117 -- Render this node
118 function Node.render(self, scope)
119         scope = scope or {}
120         scope.self = self
121
122         luci.template.render(self.template, scope)
123 end
124
125 -- Render the children
126 function Node.render_children(self, ...)
127         for k, node in ipairs(self.children) do
128                 node:render(...)
129         end
130 end
131
132
133 --[[
134 A simple template element
135 ]]--
136 Template = class(Node)
137
138 function Template.__init__(self, template)
139         Node.__init__(self)
140         self.template = template
141 end
142
143 function Template.render(self)
144         luci.template.render(self.template, {self=self})
145 end
146
147
148 --[[
149 Map - A map describing a configuration file
150 ]]--
151 Map = class(Node)
152
153 function Map.__init__(self, config, ...)
154         Node.__init__(self, ...)
155         Node._i18n(self, config, nil, nil, ...)
156
157         self.config = config
158         self.parsechain = {self.config}
159         self.template = "cbi/map"
160         if not uci.load(self.config) then
161                 error("Unable to read UCI data: " .. self.config)
162         end
163 end
164
165
166 -- Chain foreign config
167 function Map.chain(self, config)
168         table.insert(self.parsechain, config)
169 end
170
171 -- Use optimized UCI writing
172 function Map.parse(self, ...)
173         Node.parse(self, ...)
174         for i, config in ipairs(self.parsechain) do
175                 uci.save(config)
176         end
177         if luci.http.formvalue("cbi.apply") then
178                 for i, config in ipairs(self.parsechain) do
179                         uci.commit(config)
180                         if luci.config.uci_oncommit and luci.config.uci_oncommit[config] then
181                                 luci.util.exec(luci.config.uci_oncommit[config])
182                         end
183
184                         -- Refresh data because commit changes section names
185                         uci.unload(config)
186                         uci.load(config)
187                 end
188
189                 -- Reparse sections
190                 Node.parse(self, ...)
191
192         end
193         for i, config in ipairs(self.parsechain) do
194                 uci.unload(config)
195         end
196 end
197
198 -- Creates a child section
199 function Map.section(self, class, ...)
200         if instanceof(class, AbstractSection) then
201                 local obj  = class(self, ...)
202                 self:append(obj)
203                 return obj
204         else
205                 error("class must be a descendent of AbstractSection")
206         end
207 end
208
209 -- UCI add
210 function Map.add(self, sectiontype)
211         return uci.add(self.config, sectiontype)
212 end
213
214 -- UCI set
215 function Map.set(self, section, option, value)
216         if option then
217                 return uci.set(self.config, section, option, value)
218         else
219                 return uci.set(self.config, section, value)
220         end
221 end
222
223 -- UCI del
224 function Map.del(self, section, option)
225         if option then
226                 return uci.delete(self.config, section, option)
227         else
228                 return uci.delete(self.config, section)
229         end
230 end
231
232 -- UCI get
233 function Map.get(self, section, option)
234         if not section then
235                 return uci.get_all(self.config)
236         elseif option then
237                 return uci.get(self.config, section, option)
238         else
239                 return uci.get_all(self.config, section)
240         end
241 end
242
243 -- UCI stateget
244 function Map.stateget(self, section, option)
245         return uci.get_statevalue(self.config, section, option)
246 end
247
248
249 --[[
250 Page - A simple node
251 ]]--
252
253 Page = class(Node)
254 Page.__init__ = Node.__init__
255 Page.parse    = function() end
256
257
258 --[[
259 SimpleForm - A Simple non-UCI form
260 ]]--
261 SimpleForm = class(Node)
262
263 function SimpleForm.__init__(self, config, title, description, data)
264         Node.__init__(self, title, description)
265         self.config = config
266         self.data = data or {}
267         self.template = "cbi/simpleform"
268         self.dorender = true
269 end
270
271 function SimpleForm.parse(self, ...)
272         if luci.http.formvalue("cbi.submit") then
273                 Node.parse(self, 1, ...)
274         end
275                 
276         local valid = true
277         for k, j in ipairs(self.children) do 
278                 for i, v in ipairs(j.children) do
279                         valid = valid 
280                          and (not v.tag_missing or not v.tag_missing[1])
281                          and (not v.tag_invalid or not v.tag_invalid[1])
282                 end
283         end
284         
285         local state = 
286                 not luci.http.formvalue("cbi.submit") and 0
287                 or valid and 1
288                 or -1
289
290         self.dorender = self:handle(state, self.data) ~= false
291 end
292
293 function SimpleForm.render(self, ...)
294         if self.dorender then
295                 Node.render(self, ...)
296         end
297 end
298
299 function SimpleForm.section(self, class, ...)
300         if instanceof(class, AbstractSection) then
301                 local obj  = class(self, ...)
302                 self:append(obj)
303                 return obj
304         else
305                 error("class must be a descendent of AbstractSection")
306         end
307 end
308
309 -- Creates a child field
310 function SimpleForm.field(self, class, ...)
311         local section
312         for k, v in ipairs(self.children) do
313                 if instanceof(v, SimpleSection) then
314                         section = v
315                         break
316                 end
317         end
318         if not section then
319                 section = self:section(SimpleSection)
320         end
321         
322         if instanceof(class, AbstractValue) then
323                 local obj  = class(self, ...)
324                 obj.track_missing = true
325                 section:append(obj)
326                 return obj
327         else
328                 error("class must be a descendent of AbstractValue")
329         end
330 end
331
332 function SimpleForm.set(self, section, option, value)
333         self.data[option] = value
334 end
335
336
337 function SimpleForm.del(self, section, option)
338         self.data[option] = nil
339 end
340
341
342 function SimpleForm.get(self, section, option)
343         return self.data[option]
344 end
345
346
347
348 --[[
349 AbstractSection
350 ]]--
351 AbstractSection = class(Node)
352
353 function AbstractSection.__init__(self, map, sectiontype, ...)
354         Node.__init__(self, ...)
355         self.sectiontype = sectiontype
356         self.map = map
357         self.config = map.config
358         self.optionals = {}
359         self.defaults = {}
360
361         self.optional = true
362         self.addremove = false
363         self.dynamic = false
364 end
365
366 -- Appends a new option
367 function AbstractSection.option(self, class, option, ...)
368         if instanceof(class, AbstractValue) then
369                 local obj  = class(self.map, option, ...)
370
371                 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
372
373                 self:append(obj)
374                 return obj
375         else
376                 error("class must be a descendent of AbstractValue")
377         end
378 end
379
380 -- Parse optional options
381 function AbstractSection.parse_optionals(self, section)
382         if not self.optional then
383                 return
384         end
385
386         self.optionals[section] = {}
387
388         local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
389         for k,v in ipairs(self.children) do
390                 if v.optional and not v:cfgvalue(section) then
391                         if field == v.option then
392                                 field = nil
393                         else
394                                 table.insert(self.optionals[section], v)
395                         end
396                 end
397         end
398
399         if field and #field > 0 and self.dynamic then
400                 self:add_dynamic(field)
401         end
402 end
403
404 -- Add a dynamic option
405 function AbstractSection.add_dynamic(self, field, optional)
406         local o = self:option(Value, field, field)
407         o.optional = optional
408 end
409
410 -- Parse all dynamic options
411 function AbstractSection.parse_dynamic(self, section)
412         if not self.dynamic then
413                 return
414         end
415
416         local arr  = luci.util.clone(self:cfgvalue(section))
417         local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
418         for k, v in pairs(form) do
419                 arr[k] = v
420         end
421
422         for key,val in pairs(arr) do
423                 local create = true
424
425                 for i,c in ipairs(self.children) do
426                         if c.option == key then
427                                 create = false
428                         end
429                 end
430
431                 if create and key:sub(1, 1) ~= "." then
432                         self:add_dynamic(key, true)
433                 end
434         end
435 end
436
437 -- Returns the section's UCI table
438 function AbstractSection.cfgvalue(self, section)
439         return self.map:get(section)
440 end
441
442 -- Removes the section
443 function AbstractSection.remove(self, section)
444         return self.map:del(section)
445 end
446
447 -- Creates the section
448 function AbstractSection.create(self, section)
449         local stat
450         
451         if section then
452                 stat = self.map:set(section, nil, self.sectiontype)
453         else
454                 section = self.map:add(self.sectiontype)
455                 stat = section
456         end
457
458         if stat then
459                 for k,v in pairs(self.children) do
460                         if v.default then
461                                 self.map:set(section, v.option, v.default)
462                         end
463                 end
464
465                 for k,v in pairs(self.defaults) do
466                         self.map:set(section, k, v)
467                 end
468         end
469
470         return stat
471 end
472
473
474 SimpleSection = class(AbstractSection)
475
476 function SimpleSection.__init__(self, form, ...)
477         AbstractSection.__init__(self, form, nil, ...)
478         self.template = "cbi/nullsection"
479 end
480
481
482
483 --[[
484 NamedSection - A fixed configuration section defined by its name
485 ]]--
486 NamedSection = class(AbstractSection)
487
488 function NamedSection.__init__(self, map, section, type, ...)
489         AbstractSection.__init__(self, map, type, ...)
490         Node._i18n(self, map.config, section, nil, ...)
491
492         self.template = "cbi/nsection"
493         self.section = section
494         self.addremove = false
495 end
496
497 function NamedSection.parse(self)
498         local s = self.section
499         local active = self:cfgvalue(s)
500
501
502         if self.addremove then
503                 local path = self.config.."."..s
504                 if active then -- Remove the section
505                         if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
506                                 return
507                         end
508                 else           -- Create and apply default values
509                         if luci.http.formvalue("cbi.cns."..path) then
510                                 self:create(s)
511                                 return
512                         end
513                 end
514         end
515
516         if active then
517                 AbstractSection.parse_dynamic(self, s)
518                 if luci.http.formvalue("cbi.submit") then
519                         Node.parse(self, s)
520                 end
521                 AbstractSection.parse_optionals(self, s)
522         end
523 end
524
525
526 --[[
527 TypedSection - A (set of) configuration section(s) defined by the type
528         addremove:      Defines whether the user can add/remove sections of this type
529         anonymous:  Allow creating anonymous sections
530         validate:       a validation function returning nil if the section is invalid
531 ]]--
532 TypedSection = class(AbstractSection)
533
534 function TypedSection.__init__(self, map, type, ...)
535         AbstractSection.__init__(self, map, type, ...)
536         Node._i18n(self, map.config, type, nil, ...)
537
538         self.template  = "cbi/tsection"
539         self.deps = {}
540
541         self.anonymous = false
542 end
543
544 -- Return all matching UCI sections for this TypedSection
545 function TypedSection.cfgsections(self)
546         local sections = {}
547         uci.foreach(self.map.config, self.sectiontype,
548                 function (section)
549                         if self:checkscope(section[".name"]) then
550                                 table.insert(sections, section[".name"])
551                         end
552                 end)
553
554         return sections
555 end
556
557 -- Limits scope to sections that have certain option => value pairs
558 function TypedSection.depends(self, option, value)
559         table.insert(self.deps, {option=option, value=value})
560 end
561
562 function TypedSection.parse(self)
563         if self.addremove then
564                 -- Create
565                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
566                 local name  = luci.http.formvalue(crval)
567                 if self.anonymous then
568                         if name then
569                                 self:create()
570                         end
571                 else
572                         if name then
573                                 -- Ignore if it already exists
574                                 if self:cfgvalue(name) then
575                                         name = nil;
576                                 end
577
578                                 name = self:checkscope(name)
579
580                                 if not name then
581                                         self.err_invalid = true
582                                 end
583
584                                 if name and name:len() > 0 then
585                                         self:create(name)
586                                 end
587                         end
588                 end
589
590                 -- Remove
591                 crval = REMOVE_PREFIX .. self.config
592                 name = luci.http.formvaluetable(crval)
593                 for k,v in pairs(name) do
594                         if self:cfgvalue(k) and self:checkscope(k) then
595                                 self:remove(k)
596                         end
597                 end
598         end
599
600         for i, k in ipairs(self:cfgsections()) do
601                 AbstractSection.parse_dynamic(self, k)
602                 if luci.http.formvalue("cbi.submit") then
603                         Node.parse(self, k)
604                 end
605                 AbstractSection.parse_optionals(self, k)
606         end
607 end
608
609 -- Verifies scope of sections
610 function TypedSection.checkscope(self, section)
611         -- Check if we are not excluded
612         if self.filter and not self:filter(section) then
613                 return nil
614         end
615
616         -- Check if at least one dependency is met
617         if #self.deps > 0 and self:cfgvalue(section) then
618                 local stat = false
619
620                 for k, v in ipairs(self.deps) do
621                         if self:cfgvalue(section)[v.option] == v.value then
622                                 stat = true
623                         end
624                 end
625
626                 if not stat then
627                         return nil
628                 end
629         end
630
631         return self:validate(section)
632 end
633
634
635 -- Dummy validate function
636 function TypedSection.validate(self, section)
637         return section
638 end
639
640
641 --[[
642 AbstractValue - An abstract Value Type
643         null:           Value can be empty
644         valid:          A function returning the value if it is valid otherwise nil
645         depends:        A table of option => value pairs of which one must be true
646         default:        The default value
647         size:           The size of the input fields
648         rmempty:        Unset value if empty
649         optional:       This value is optional (see AbstractSection.optionals)
650 ]]--
651 AbstractValue = class(Node)
652
653 function AbstractValue.__init__(self, map, option, ...)
654         Node.__init__(self, ...)
655         self.option = option
656         self.map    = map
657         self.config = map.config
658         self.tag_invalid = {}
659         self.tag_missing = {}
660         self.deps = {}
661
662         self.track_missing = false
663         self.rmempty   = false
664         self.default   = nil
665         self.size      = nil
666         self.optional  = false
667         self.stateful  = false
668 end
669
670 -- Add a dependencie to another section field
671 function AbstractValue.depends(self, field, value)
672         table.insert(self.deps, {field=field, value=value})
673 end
674
675 -- Return whether this object should be created
676 function AbstractValue.formcreated(self, section)
677         local key = "cbi.opt."..self.config.."."..section
678         return (luci.http.formvalue(key) == self.option)
679 end
680
681 -- Returns the formvalue for this object
682 function AbstractValue.formvalue(self, section)
683         local key = "cbid."..self.map.config.."."..section.."."..self.option
684         return luci.http.formvalue(key)
685 end
686
687 function AbstractValue.additional(self, value)
688         self.optional = value
689 end
690
691 function AbstractValue.mandatory(self, value)
692         self.rmempty = not value
693 end
694
695 function AbstractValue.parse(self, section)
696         local fvalue = self:formvalue(section)
697         local cvalue = self:cfgvalue(section)
698
699         if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
700                 fvalue = self:transform(self:validate(fvalue, section))
701                 if not fvalue then
702                         self.tag_invalid[section] = true
703                 end
704                 if fvalue and not (fvalue == cvalue) then
705                         self:write(section, fvalue)
706                 end
707         else                                                    -- Unset the UCI or error
708                 if self.rmempty or self.optional then
709                         self:remove(section)
710                 elseif self.track_missing and not fvalue or fvalue ~= cvalue then
711                         self.tag_missing[section] = true
712                 end
713         end
714 end
715
716 -- Render if this value exists or if it is mandatory
717 function AbstractValue.render(self, s, scope)
718         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
719                 scope = scope or {}
720                 scope.section = s
721                 scope.cbid    = "cbid." .. self.config ..
722                                 "."     .. s           ..
723                                                 "."     .. self.option
724
725                 scope.ifattr = function(cond,key,val)
726                         if cond then
727                                 return string.format(
728                                         ' %s="%s"', tostring(key),
729                                         tostring( val
730                                          or scope[key]
731                                          or (type(self[key]) ~= "function" and self[key])
732                                          or "" )
733                                 )
734                         else
735                                 return ''
736                         end
737                 end
738
739                 scope.attr = function(...)
740                         return scope.ifattr( true, ... )
741                 end
742
743                 Node.render(self, scope)
744         end
745 end
746
747 -- Return the UCI value of this object
748 function AbstractValue.cfgvalue(self, section)
749         return self.stateful
750          and self.map:stateget(section, self.option)
751          or  self.map:get(section, self.option)
752 end
753
754 -- Validate the form value
755 function AbstractValue.validate(self, value)
756         return value
757 end
758
759 AbstractValue.transform = AbstractValue.validate
760
761
762 -- Write to UCI
763 function AbstractValue.write(self, section, value)
764         return self.map:set(section, self.option, value)
765 end
766
767 -- Remove from UCI
768 function AbstractValue.remove(self, section)
769         return self.map:del(section, self.option)
770 end
771
772
773
774
775 --[[
776 Value - A one-line value
777         maxlength:      The maximum length
778 ]]--
779 Value = class(AbstractValue)
780
781 function Value.__init__(self, ...)
782         AbstractValue.__init__(self, ...)
783         self.template  = "cbi/value"
784         self.keylist = {}
785         self.vallist = {}
786 end
787
788 function Value.value(self, key, val)
789         val = val or key
790         table.insert(self.keylist, tostring(key))
791         table.insert(self.vallist, tostring(val))
792 end
793
794
795 -- DummyValue - This does nothing except being there
796 DummyValue = class(AbstractValue)
797
798 function DummyValue.__init__(self, map, ...)
799         AbstractValue.__init__(self, map, ...)
800         self.template = "cbi/dvalue"
801         self.value = nil
802 end
803
804 function DummyValue.parse(self)
805
806 end
807
808
809 --[[
810 Flag - A flag being enabled or disabled
811 ]]--
812 Flag = class(AbstractValue)
813
814 function Flag.__init__(self, ...)
815         AbstractValue.__init__(self, ...)
816         self.template  = "cbi/fvalue"
817
818         self.enabled = "1"
819         self.disabled = "0"
820 end
821
822 -- A flag can only have two states: set or unset
823 function Flag.parse(self, section)
824         local fvalue = self:formvalue(section)
825
826         if fvalue then
827                 fvalue = self.enabled
828         else
829                 fvalue = self.disabled
830         end
831
832         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
833                 if not(fvalue == self:cfgvalue(section)) then
834                         self:write(section, fvalue)
835                 end
836         else
837                 self:remove(section)
838         end
839 end
840
841
842
843 --[[
844 ListValue - A one-line value predefined in a list
845         widget: The widget that will be used (select, radio)
846 ]]--
847 ListValue = class(AbstractValue)
848
849 function ListValue.__init__(self, ...)
850         AbstractValue.__init__(self, ...)
851         self.template  = "cbi/lvalue"
852         self.keylist = {}
853         self.vallist = {}
854
855         self.size   = 1
856         self.widget = "select"
857 end
858
859 function ListValue.value(self, key, val)
860         val = val or key
861         table.insert(self.keylist, tostring(key))
862         table.insert(self.vallist, tostring(val))
863 end
864
865 function ListValue.validate(self, val)
866         if luci.util.contains(self.keylist, val) then
867                 return val
868         else
869                 return nil
870         end
871 end
872
873
874
875 --[[
876 MultiValue - Multiple delimited values
877         widget: The widget that will be used (select, checkbox)
878         delimiter: The delimiter that will separate the values (default: " ")
879 ]]--
880 MultiValue = class(AbstractValue)
881
882 function MultiValue.__init__(self, ...)
883         AbstractValue.__init__(self, ...)
884         self.template = "cbi/mvalue"
885         self.keylist = {}
886         self.vallist = {}
887
888         self.widget = "checkbox"
889         self.delimiter = " "
890 end
891
892 function MultiValue.render(self, ...)
893         if self.widget == "select" and not self.size then
894                 self.size = #self.vallist
895         end
896
897         AbstractValue.render(self, ...)
898 end
899
900 function MultiValue.value(self, key, val)
901         val = val or key
902         table.insert(self.keylist, tostring(key))
903         table.insert(self.vallist, tostring(val))
904 end
905
906 function MultiValue.valuelist(self, section)
907         local val = self:cfgvalue(section)
908
909         if not(type(val) == "string") then
910                 return {}
911         end
912
913         return luci.util.split(val, self.delimiter)
914 end
915
916 function MultiValue.validate(self, val)
917         val = (type(val) == "table") and val or {val}
918
919         local result
920
921         for i, value in ipairs(val) do
922                 if luci.util.contains(self.keylist, value) then
923                         result = result and (result .. self.delimiter .. value) or value
924                 end
925         end
926
927         return result
928 end
929
930 --[[
931 TextValue - A multi-line value
932         rows:   Rows
933 ]]--
934 TextValue = class(AbstractValue)
935
936 function TextValue.__init__(self, ...)
937         AbstractValue.__init__(self, ...)
938         self.template  = "cbi/tvalue"
939 end