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