Converted IPKG configuration to CBI model
[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 SimpleForm - A Simple non-UCI form
251 ]]--
252 SimpleForm = class(Node)
253
254 function SimpleForm.__init__(self, config, title, description, data)
255         Node.__init__(self, title, description)
256         self.config = config
257         self.data = data or {}
258         self.template = "cbi/simpleform"
259         self.dorender = true
260 end
261
262 function SimpleForm.parse(self, ...)
263         Node.parse(self, 1, ...)
264                 
265         local valid = true
266         for i, v in ipairs(self.children) do
267                 valid = valid 
268                  and (not v.tag_missing or not v.tag_missing[1])
269                  and (not v.tag_invalid or not v.tag_invalid[1])
270         end
271         
272         local state = 
273                 not luci.http.formvalue("cbi.submit") and 0
274                 or valid and 1
275                 or -1
276
277         self.dorender = self:handle(state, self.data)
278 end
279
280 function SimpleForm.render(self, ...)
281         if self.dorender then
282                 Node.render(self, ...)
283         end
284 end
285
286 -- Creates a child section
287 function SimpleForm.field(self, class, ...)
288         if instanceof(class, AbstractValue) then
289                 local obj  = class(self, ...)
290                 self:append(obj)
291                 return obj
292         else
293                 error("class must be a descendent of AbstractValue")
294         end
295 end
296
297 function SimpleForm.set(self, section, option, value)
298         self.data[option] = value
299 end
300
301
302 function SimpleForm.del(self, section, option)
303         self.data[option] = nil
304 end
305
306
307 function SimpleForm.get(self, section, option)
308         return self.data[option]
309 end
310
311
312
313 --[[
314 AbstractSection
315 ]]--
316 AbstractSection = class(Node)
317
318 function AbstractSection.__init__(self, map, sectiontype, ...)
319         Node.__init__(self, ...)
320         self.sectiontype = sectiontype
321         self.map = map
322         self.config = map.config
323         self.optionals = {}
324         self.defaults = {}
325
326         self.optional = true
327         self.addremove = false
328         self.dynamic = false
329 end
330
331 -- Appends a new option
332 function AbstractSection.option(self, class, option, ...)
333         if instanceof(class, AbstractValue) then
334                 local obj  = class(self.map, option, ...)
335
336                 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
337
338                 self:append(obj)
339                 return obj
340         else
341                 error("class must be a descendent of AbstractValue")
342         end
343 end
344
345 -- Parse optional options
346 function AbstractSection.parse_optionals(self, section)
347         if not self.optional then
348                 return
349         end
350
351         self.optionals[section] = {}
352
353         local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
354         for k,v in ipairs(self.children) do
355                 if v.optional and not v:cfgvalue(section) then
356                         if field == v.option then
357                                 field = nil
358                         else
359                                 table.insert(self.optionals[section], v)
360                         end
361                 end
362         end
363
364         if field and #field > 0 and self.dynamic then
365                 self:add_dynamic(field)
366         end
367 end
368
369 -- Add a dynamic option
370 function AbstractSection.add_dynamic(self, field, optional)
371         local o = self:option(Value, field, field)
372         o.optional = optional
373 end
374
375 -- Parse all dynamic options
376 function AbstractSection.parse_dynamic(self, section)
377         if not self.dynamic then
378                 return
379         end
380
381         local arr  = luci.util.clone(self:cfgvalue(section))
382         local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
383         for k, v in pairs(form) do
384                 arr[k] = v
385         end
386
387         for key,val in pairs(arr) do
388                 local create = true
389
390                 for i,c in ipairs(self.children) do
391                         if c.option == key then
392                                 create = false
393                         end
394                 end
395
396                 if create and key:sub(1, 1) ~= "." then
397                         self:add_dynamic(key, true)
398                 end
399         end
400 end
401
402 -- Returns the section's UCI table
403 function AbstractSection.cfgvalue(self, section)
404         return self.map:get(section)
405 end
406
407 -- Removes the section
408 function AbstractSection.remove(self, section)
409         return self.map:del(section)
410 end
411
412 -- Creates the section
413 function AbstractSection.create(self, section)
414         local stat
415         
416         if section then
417                 stat = self.map:set(section, nil, self.sectiontype)
418         else
419                 section = self.map:add(self.sectiontype)
420                 stat = section
421         end
422
423         if stat then
424                 for k,v in pairs(self.children) do
425                         if v.default then
426                                 self.map:set(section, v.option, v.default)
427                         end
428                 end
429
430                 for k,v in pairs(self.defaults) do
431                         self.map:set(section, k, v)
432                 end
433         end
434
435         return stat
436 end
437
438
439
440 --[[
441 NamedSection - A fixed configuration section defined by its name
442 ]]--
443 NamedSection = class(AbstractSection)
444
445 function NamedSection.__init__(self, map, section, type, ...)
446         AbstractSection.__init__(self, map, type, ...)
447         Node._i18n(self, map.config, section, nil, ...)
448
449         self.template = "cbi/nsection"
450         self.section = section
451         self.addremove = false
452 end
453
454 function NamedSection.parse(self)
455         local s = self.section
456         local active = self:cfgvalue(s)
457
458
459         if self.addremove then
460                 local path = self.config.."."..s
461                 if active then -- Remove the section
462                         if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
463                                 return
464                         end
465                 else           -- Create and apply default values
466                         if luci.http.formvalue("cbi.cns."..path) then
467                                 self:create(s)
468                                 return
469                         end
470                 end
471         end
472
473         if active then
474                 AbstractSection.parse_dynamic(self, s)
475                 if luci.http.formvalue("cbi.submit") then
476                         Node.parse(self, s)
477                 end
478                 AbstractSection.parse_optionals(self, s)
479         end
480 end
481
482
483 --[[
484 TypedSection - A (set of) configuration section(s) defined by the type
485         addremove:      Defines whether the user can add/remove sections of this type
486         anonymous:  Allow creating anonymous sections
487         validate:       a validation function returning nil if the section is invalid
488 ]]--
489 TypedSection = class(AbstractSection)
490
491 function TypedSection.__init__(self, map, type, ...)
492         AbstractSection.__init__(self, map, type, ...)
493         Node._i18n(self, map.config, type, nil, ...)
494
495         self.template  = "cbi/tsection"
496         self.deps = {}
497
498         self.anonymous = false
499 end
500
501 -- Return all matching UCI sections for this TypedSection
502 function TypedSection.cfgsections(self)
503         local sections = {}
504         uci.foreach(self.map.config, self.sectiontype,
505                 function (section)
506                         if self:checkscope(section[".name"]) then
507                                 table.insert(sections, section[".name"])
508                         end
509                 end)
510
511         return sections
512 end
513
514 -- Limits scope to sections that have certain option => value pairs
515 function TypedSection.depends(self, option, value)
516         table.insert(self.deps, {option=option, value=value})
517 end
518
519 function TypedSection.parse(self)
520         if self.addremove then
521                 -- Create
522                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
523                 local name  = luci.http.formvalue(crval)
524                 if self.anonymous then
525                         if name then
526                                 self:create()
527                         end
528                 else
529                         if name then
530                                 -- Ignore if it already exists
531                                 if self:cfgvalue(name) then
532                                         name = nil;
533                                 end
534
535                                 name = self:checkscope(name)
536
537                                 if not name then
538                                         self.err_invalid = true
539                                 end
540
541                                 if name and name:len() > 0 then
542                                         self:create(name)
543                                 end
544                         end
545                 end
546
547                 -- Remove
548                 crval = REMOVE_PREFIX .. self.config
549                 name = luci.http.formvaluetable(crval)
550                 for k,v in pairs(name) do
551                         if self:cfgvalue(k) and self:checkscope(k) then
552                                 self:remove(k)
553                         end
554                 end
555         end
556
557         for i, k in ipairs(self:cfgsections()) do
558                 AbstractSection.parse_dynamic(self, k)
559                 if luci.http.formvalue("cbi.submit") then
560                         Node.parse(self, k)
561                 end
562                 AbstractSection.parse_optionals(self, k)
563         end
564 end
565
566 -- Verifies scope of sections
567 function TypedSection.checkscope(self, section)
568         -- Check if we are not excluded
569         if self.filter and not self:filter(section) then
570                 return nil
571         end
572
573         -- Check if at least one dependency is met
574         if #self.deps > 0 and self:cfgvalue(section) then
575                 local stat = false
576
577                 for k, v in ipairs(self.deps) do
578                         if self:cfgvalue(section)[v.option] == v.value then
579                                 stat = true
580                         end
581                 end
582
583                 if not stat then
584                         return nil
585                 end
586         end
587
588         return self:validate(section)
589 end
590
591
592 -- Dummy validate function
593 function TypedSection.validate(self, section)
594         return section
595 end
596
597
598 --[[
599 AbstractValue - An abstract Value Type
600         null:           Value can be empty
601         valid:          A function returning the value if it is valid otherwise nil
602         depends:        A table of option => value pairs of which one must be true
603         default:        The default value
604         size:           The size of the input fields
605         rmempty:        Unset value if empty
606         optional:       This value is optional (see AbstractSection.optionals)
607 ]]--
608 AbstractValue = class(Node)
609
610 function AbstractValue.__init__(self, map, option, ...)
611         Node.__init__(self, ...)
612         self.option = option
613         self.map    = map
614         self.config = map.config
615         self.tag_invalid = {}
616         self.tag_missing = {}
617         self.deps = {}
618
619         self.rmempty   = false
620         self.default   = nil
621         self.size      = nil
622         self.optional  = false
623         self.stateful  = false
624 end
625
626 -- Add a dependencie to another section field
627 function AbstractValue.depends(self, field, value)
628         table.insert(self.deps, {field=field, value=value})
629 end
630
631 -- Return whether this object should be created
632 function AbstractValue.formcreated(self, section)
633         local key = "cbi.opt."..self.config.."."..section
634         return (luci.http.formvalue(key) == self.option)
635 end
636
637 -- Returns the formvalue for this object
638 function AbstractValue.formvalue(self, section)
639         local key = "cbid."..self.map.config.."."..section.."."..self.option
640         return luci.http.formvalue(key)
641 end
642
643 function AbstractValue.additional(self, value)
644         self.optional = value
645 end
646
647 function AbstractValue.mandatory(self, value)
648         self.rmempty = not value
649 end
650
651 function AbstractValue.parse(self, section)
652         local fvalue = self:formvalue(section)
653         local cvalue = self:cfgvalue(section)
654
655         if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
656                 fvalue = self:transform(self:validate(fvalue))
657                 if not fvalue then
658                         self.tag_invalid[section] = true
659                 end
660                 if fvalue and not (fvalue == self:cfgvalue(section)) then
661                         self:write(section, fvalue)
662                 end
663         else                                                    -- Unset the UCI or error
664                 if self.rmempty or self.optional then
665                         self:remove(section)
666                 elseif not fvalue or fvalue ~= cvalue then
667                         --self.tag_missing[section] = true
668                 end
669         end
670 end
671
672 -- Render if this value exists or if it is mandatory
673 function AbstractValue.render(self, s, scope)
674         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
675                 scope = scope or {}
676                 scope.section = s
677                 scope.cbid    = "cbid." .. self.config ..
678                                 "."     .. s           ..
679                                                 "."     .. self.option
680
681                 scope.ifattr = function(cond,key,val)
682                         if cond then
683                                 return string.format(
684                                         ' %s="%s"', tostring(key),
685                                         tostring( val
686                                          or scope[key]
687                                          or (type(self[key]) ~= "function" and self[key])
688                                          or "" )
689                                 )
690                         else
691                                 return ''
692                         end
693                 end
694
695                 scope.attr = function(...)
696                         return scope.ifattr( true, ... )
697                 end
698
699                 Node.render(self, scope)
700         end
701 end
702
703 -- Return the UCI value of this object
704 function AbstractValue.cfgvalue(self, section)
705         return self.stateful
706          and self.map:stateget(section, self.option)
707          or  self.map:get(section, self.option)
708 end
709
710 -- Validate the form value
711 function AbstractValue.validate(self, value)
712         return value
713 end
714
715 AbstractValue.transform = AbstractValue.validate
716
717
718 -- Write to UCI
719 function AbstractValue.write(self, section, value)
720         return self.map:set(section, self.option, value)
721 end
722
723 -- Remove from UCI
724 function AbstractValue.remove(self, section)
725         return self.map:del(section, self.option)
726 end
727
728
729
730
731 --[[
732 Value - A one-line value
733         maxlength:      The maximum length
734 ]]--
735 Value = class(AbstractValue)
736
737 function Value.__init__(self, ...)
738         AbstractValue.__init__(self, ...)
739         self.template  = "cbi/value"
740         self.keylist = {}
741         self.vallist = {}
742 end
743
744 function Value.value(self, key, val)
745         val = val or key
746         table.insert(self.keylist, tostring(key))
747         table.insert(self.vallist, tostring(val))
748 end
749
750
751 -- DummyValue - This does nothing except being there
752 DummyValue = class(AbstractValue)
753
754 function DummyValue.__init__(self, map, ...)
755         AbstractValue.__init__(self, map, ...)
756         self.template = "cbi/dvalue"
757         self.value = nil
758 end
759
760 function DummyValue.parse(self)
761
762 end
763
764
765 --[[
766 Flag - A flag being enabled or disabled
767 ]]--
768 Flag = class(AbstractValue)
769
770 function Flag.__init__(self, ...)
771         AbstractValue.__init__(self, ...)
772         self.template  = "cbi/fvalue"
773
774         self.enabled = "1"
775         self.disabled = "0"
776 end
777
778 -- A flag can only have two states: set or unset
779 function Flag.parse(self, section)
780         local fvalue = self:formvalue(section)
781
782         if fvalue then
783                 fvalue = self.enabled
784         else
785                 fvalue = self.disabled
786         end
787
788         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
789                 if not(fvalue == self:cfgvalue(section)) then
790                         self:write(section, fvalue)
791                 end
792         else
793                 self:remove(section)
794         end
795 end
796
797
798
799 --[[
800 ListValue - A one-line value predefined in a list
801         widget: The widget that will be used (select, radio)
802 ]]--
803 ListValue = class(AbstractValue)
804
805 function ListValue.__init__(self, ...)
806         AbstractValue.__init__(self, ...)
807         self.template  = "cbi/lvalue"
808         self.keylist = {}
809         self.vallist = {}
810
811         self.size   = 1
812         self.widget = "select"
813 end
814
815 function ListValue.value(self, key, val)
816         val = val or key
817         table.insert(self.keylist, tostring(key))
818         table.insert(self.vallist, tostring(val))
819 end
820
821 function ListValue.validate(self, val)
822         if luci.util.contains(self.keylist, val) then
823                 return val
824         else
825                 return nil
826         end
827 end
828
829
830
831 --[[
832 MultiValue - Multiple delimited values
833         widget: The widget that will be used (select, checkbox)
834         delimiter: The delimiter that will separate the values (default: " ")
835 ]]--
836 MultiValue = class(AbstractValue)
837
838 function MultiValue.__init__(self, ...)
839         AbstractValue.__init__(self, ...)
840         self.template = "cbi/mvalue"
841         self.keylist = {}
842         self.vallist = {}
843
844         self.widget = "checkbox"
845         self.delimiter = " "
846 end
847
848 function MultiValue.render(self, ...)
849         if self.widget == "select" and not self.size then
850                 self.size = #self.vallist
851         end
852
853         AbstractValue.render(self, ...)
854 end
855
856 function MultiValue.value(self, key, val)
857         val = val or key
858         table.insert(self.keylist, tostring(key))
859         table.insert(self.vallist, tostring(val))
860 end
861
862 function MultiValue.valuelist(self, section)
863         local val = self:cfgvalue(section)
864
865         if not(type(val) == "string") then
866                 return {}
867         end
868
869         return luci.util.split(val, self.delimiter)
870 end
871
872 function MultiValue.validate(self, val)
873         val = (type(val) == "table") and val or {val}
874
875         local result
876
877         for i, value in ipairs(val) do
878                 if luci.util.contains(self.keylist, value) then
879                         result = result and (result .. self.delimiter .. value) or value
880                 end
881         end
882
883         return result
884 end
885
886 --[[
887 TextValue - A multi-line value
888         rows:   Rows
889 ]]--
890 TextValue = class(AbstractValue)
891
892 function TextValue.__init__(self, ...)
893         AbstractValue.__init__(self, ...)
894         self.template  = "cbi/tvalue"
895 end