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