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