libs/cbi: Create section after validation to avoid validation errors with empty fields
[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.uvl")
33
34 local uci = require("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 AUTO = true
43
44 CREATE_PREFIX = "cbi.cts."
45 REMOVE_PREFIX = "cbi.rts."
46
47 -- Loads a CBI map from given file, creating an environment and returns it
48 function load(cbimap, ...)
49 require("luci.fs")
50 require("luci.i18n")
51 require("luci.config")
52 require("luci.util")
53
54 local cbidir = luci.util.libpath() .. "/model/cbi/"
55 local func, err = loadfile(cbidir..cbimap..".lua")
56
57 if not func then
58 return nil
59 end
60
61 luci.i18n.loadc("cbi")
62
63 luci.util.resfenv(func)
64 luci.util.updfenv(func, luci.cbi)
65 luci.util.extfenv(func, "translate", luci.i18n.translate)
66 luci.util.extfenv(func, "translatef", luci.i18n.translatef)
67 luci.util.extfenv(func, "arg", {...})
68
69 local maps = {func()}
70
71 for i, map in ipairs(maps) do
72 if not instanceof(map, Node) then
73 error("CBI map returns no valid map object!")
74 return nil
75 end
76 end
77
78 return maps
79 end
80
81
82 function _uvl_strip_remote_dependencies(deps)
83 local clean = {}
84
85 for k, v in pairs(deps) do
86 k = k:gsub("%$config%.%$section%.", "")
87 if k:match("^[%w_]+$") and type(v) == "string" then
88 clean[k] = v
89 end
90 end
91
92 return clean
93 end
94
95
96 -- Node pseudo abstract class
97 Node = class()
98
99 function Node.__init__(self, title, description)
100 self.children = {}
101 self.title = title or ""
102 self.description = description or ""
103 self.template = "cbi/node"
104 end
105
106 -- i18n helper
107 function Node._i18n(self, config, section, option, title, description)
108
109 -- i18n loaded?
110 if type(luci.i18n) == "table" then
111
112 local key = config and config:gsub("[^%w]+", "") or ""
113
114 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
115 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
116
117 self.title = title or luci.i18n.translate( key, option or section or config )
118 self.description = description or luci.i18n.translate( key .. "_desc", "" )
119 end
120 end
121
122 -- Append child nodes
123 function Node.append(self, obj)
124 table.insert(self.children, obj)
125 end
126
127 -- Parse this node and its children
128 function Node.parse(self, ...)
129 for k, child in ipairs(self.children) do
130 child:parse(...)
131 end
132 end
133
134 -- Render this node
135 function Node.render(self, scope)
136 scope = scope or {}
137 scope.self = self
138
139 luci.template.render(self.template, scope)
140 end
141
142 -- Render the children
143 function Node.render_children(self, ...)
144 for k, node in ipairs(self.children) do
145 node:render(...)
146 end
147 end
148
149
150 --[[
151 A simple template element
152 ]]--
153 Template = class(Node)
154
155 function Template.__init__(self, template)
156 Node.__init__(self)
157 self.template = template
158 end
159
160 function Template.render(self)
161 luci.template.render(self.template, {self=self})
162 end
163
164
165 --[[
166 Map - A map describing a configuration file
167 ]]--
168 Map = class(Node)
169
170 function Map.__init__(self, config, ...)
171 Node.__init__(self, ...)
172 Node._i18n(self, config, nil, nil, ...)
173
174 self.config = config
175 self.parsechain = {self.config}
176 self.template = "cbi/map"
177 self.uci = uci.cursor()
178 self.save = true
179 if not self.uci:load(self.config) then
180 error("Unable to read UCI data: " .. self.config)
181 end
182
183 self.validator = luci.uvl.UVL()
184 self.scheme = self.validator:get_scheme(self.config)
185
186 end
187
188 function Map.get_scheme(self, sectiontype, option)
189 if not option then
190 return self.scheme and self.scheme.sections[sectiontype]
191 else
192 return self.scheme and self.scheme.variables[sectiontype]
193 and self.scheme.variables[sectiontype][option]
194 end
195 end
196
197
198 -- Chain foreign config
199 function Map.chain(self, config)
200 table.insert(self.parsechain, config)
201 end
202
203 -- Use optimized UCI writing
204 function Map.parse(self, ...)
205 Node.parse(self, ...)
206
207 if self.save then
208 for i, config in ipairs(self.parsechain) do
209 self.uci:save(config)
210 end
211 if luci.http.formvalue("cbi.apply") then
212 for i, config in ipairs(self.parsechain) do
213 self.uci:commit(config)
214 self.uci:apply(config)
215
216 -- Refresh data because commit changes section names
217 self.uci:load(config)
218 end
219
220 -- Reparse sections
221 Node.parse(self, ...)
222
223 end
224 for i, config in ipairs(self.parsechain) do
225 self.uci:unload(config)
226 end
227 end
228 end
229
230 -- Creates a child section
231 function Map.section(self, class, ...)
232 if instanceof(class, AbstractSection) then
233 local obj = class(self, ...)
234 self:append(obj)
235 return obj
236 else
237 error("class must be a descendent of AbstractSection")
238 end
239 end
240
241 -- UCI add
242 function Map.add(self, sectiontype)
243 return self.uci:add(self.config, sectiontype)
244 end
245
246 -- UCI set
247 function Map.set(self, section, option, value)
248 if option then
249 return self.uci:set(self.config, section, option, value)
250 else
251 return self.uci:set(self.config, section, value)
252 end
253 end
254
255 -- UCI del
256 function Map.del(self, section, option)
257 if option then
258 return self.uci:delete(self.config, section, option)
259 else
260 return self.uci:delete(self.config, section)
261 end
262 end
263
264 -- UCI get
265 function Map.get(self, section, option)
266 if not section then
267 return self.uci:get_all(self.config)
268 elseif option then
269 return self.uci:get(self.config, section, option)
270 else
271 return self.uci:get_all(self.config, section)
272 end
273 end
274
275
276 --[[
277 Page - A simple node
278 ]]--
279
280 Page = class(Node)
281 Page.__init__ = Node.__init__
282 Page.parse = function() end
283
284
285 --[[
286 SimpleForm - A Simple non-UCI form
287 ]]--
288 SimpleForm = class(Node)
289
290 function SimpleForm.__init__(self, config, title, description, data)
291 Node.__init__(self, title, description)
292 self.config = config
293 self.data = data or {}
294 self.template = "cbi/simpleform"
295 self.dorender = true
296 end
297
298 function SimpleForm.parse(self, ...)
299 if luci.http.formvalue("cbi.submit") then
300 Node.parse(self, 1, ...)
301 end
302
303 local valid = true
304 for k, j in ipairs(self.children) do
305 for i, v in ipairs(j.children) do
306 valid = valid
307 and (not v.tag_missing or not v.tag_missing[1])
308 and (not v.tag_invalid or not v.tag_invalid[1])
309 end
310 end
311
312 local state =
313 not luci.http.formvalue("cbi.submit") and 0
314 or valid and 1
315 or -1
316
317 self.dorender = not self.handle or self:handle(state, self.data) ~= false
318 end
319
320 function SimpleForm.render(self, ...)
321 if self.dorender then
322 Node.render(self, ...)
323 end
324 end
325
326 function SimpleForm.section(self, class, ...)
327 if instanceof(class, AbstractSection) then
328 local obj = class(self, ...)
329 self:append(obj)
330 return obj
331 else
332 error("class must be a descendent of AbstractSection")
333 end
334 end
335
336 -- Creates a child field
337 function SimpleForm.field(self, class, ...)
338 local section
339 for k, v in ipairs(self.children) do
340 if instanceof(v, SimpleSection) then
341 section = v
342 break
343 end
344 end
345 if not section then
346 section = self:section(SimpleSection)
347 end
348
349 if instanceof(class, AbstractValue) then
350 local obj = class(self, section, ...)
351 obj.track_missing = true
352 section:append(obj)
353 return obj
354 else
355 error("class must be a descendent of AbstractValue")
356 end
357 end
358
359 function SimpleForm.set(self, section, option, value)
360 self.data[option] = value
361 end
362
363
364 function SimpleForm.del(self, section, option)
365 self.data[option] = nil
366 end
367
368
369 function SimpleForm.get(self, section, option)
370 return self.data[option]
371 end
372
373
374 function SimpleForm.get_scheme()
375 return nil
376 end
377
378
379
380 --[[
381 AbstractSection
382 ]]--
383 AbstractSection = class(Node)
384
385 function AbstractSection.__init__(self, map, sectiontype, ...)
386 Node.__init__(self, ...)
387 self.sectiontype = sectiontype
388 self.map = map
389 self.config = map.config
390 self.optionals = {}
391 self.defaults = {}
392 self.fields = {}
393 self.tag_error = {}
394 self.tag_invalid = {}
395 self.tag_deperror = {}
396
397 self.optional = true
398 self.addremove = false
399 self.dynamic = false
400 end
401
402 -- Appends a new option
403 function AbstractSection.option(self, class, option, ...)
404 -- Autodetect from UVL
405 if class == true and self.map:get_scheme(self.sectiontype, option) then
406 local vs = self.map:get_scheme(self.sectiontype, option)
407 if vs.type == "boolean" then
408 class = Flag
409 elseif vs.type == "list" then
410 class = DynamicList
411 elseif vs.type == "enum" or vs.type == "reference" then
412 class = ListValue
413 else
414 class = Value
415 end
416 end
417
418 if instanceof(class, AbstractValue) then
419 local obj = class(self.map, self, option, ...)
420
421 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
422
423 self:append(obj)
424 self.fields[option] = obj
425 return obj
426 elseif class == true then
427 error("No valid class was given and autodetection failed.")
428 else
429 error("class must be a descendant of AbstractValue")
430 end
431 end
432
433 -- Parse optional options
434 function AbstractSection.parse_optionals(self, section)
435 if not self.optional then
436 return
437 end
438
439 self.optionals[section] = {}
440
441 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
442 for k,v in ipairs(self.children) do
443 if v.optional and not v:cfgvalue(section) then
444 if field == v.option then
445 field = nil
446 else
447 table.insert(self.optionals[section], v)
448 end
449 end
450 end
451
452 if field and #field > 0 and self.dynamic then
453 self:add_dynamic(field)
454 end
455 end
456
457 -- Add a dynamic option
458 function AbstractSection.add_dynamic(self, field, optional)
459 local o = self:option(Value, field, field)
460 o.optional = optional
461 end
462
463 -- Parse all dynamic options
464 function AbstractSection.parse_dynamic(self, section)
465 if not self.dynamic then
466 return
467 end
468
469 local arr = luci.util.clone(self:cfgvalue(section))
470 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
471 for k, v in pairs(form) do
472 arr[k] = v
473 end
474
475 for key,val in pairs(arr) do
476 local create = true
477
478 for i,c in ipairs(self.children) do
479 if c.option == key then
480 create = false
481 end
482 end
483
484 if create and key:sub(1, 1) ~= "." then
485 self:add_dynamic(key, true)
486 end
487 end
488 end
489
490 -- Returns the section's UCI table
491 function AbstractSection.cfgvalue(self, section)
492 return self.map:get(section)
493 end
494
495 -- Removes the section
496 function AbstractSection.remove(self, section)
497 return self.map:del(section)
498 end
499
500 -- Creates the section
501 function AbstractSection.create(self, section)
502 local stat
503
504 if section then
505 stat = self.map:set(section, nil, self.sectiontype)
506 else
507 section = self.map:add(self.sectiontype)
508 stat = section
509 end
510
511 if stat then
512 for k,v in pairs(self.children) do
513 if v.default then
514 self.map:set(section, v.option, v.default)
515 end
516 end
517
518 for k,v in pairs(self.defaults) do
519 self.map:set(section, k, v)
520 end
521 end
522
523 return stat
524 end
525
526
527 SimpleSection = class(AbstractSection)
528
529 function SimpleSection.__init__(self, form, ...)
530 AbstractSection.__init__(self, form, nil, ...)
531 self.template = "cbi/nullsection"
532 end
533
534
535 Table = class(AbstractSection)
536
537 function Table.__init__(self, form, data, ...)
538 local datasource = {}
539 datasource.config = "table"
540 self.data = data
541
542 function datasource.get(self, section, option)
543 return data[section] and data[section][option]
544 end
545
546 function datasource.del(...)
547 return true
548 end
549
550 function datasource.get_scheme()
551 return nil
552 end
553
554 AbstractSection.__init__(self, datasource, "table", ...)
555 self.template = "cbi/tblsection"
556 self.rowcolors = true
557 self.anonymous = true
558 end
559
560 function Table.parse(self)
561 for i, k in ipairs(self:cfgsections()) do
562 if luci.http.formvalue("cbi.submit") then
563 Node.parse(self, k)
564 end
565 end
566 end
567
568 function Table.cfgsections(self)
569 local sections = {}
570
571 for i, v in luci.util.kspairs(self.data) do
572 table.insert(sections, i)
573 end
574
575 return sections
576 end
577
578
579
580 --[[
581 NamedSection - A fixed configuration section defined by its name
582 ]]--
583 NamedSection = class(AbstractSection)
584
585 function NamedSection.__init__(self, map, section, stype, ...)
586 AbstractSection.__init__(self, map, stype, ...)
587 Node._i18n(self, map.config, section, nil, ...)
588
589 -- Defaults
590 self.addremove = false
591
592 -- Use defaults from UVL
593 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
594 local vs = self.map:get_scheme(self.sectiontype)
595 self.addremove = not vs.unique and not vs.required
596 self.dynamic = vs.dynamic
597 self.title = self.title or vs.title
598 self.description = self.description or vs.descr
599 end
600
601 self.template = "cbi/nsection"
602 self.section = section
603 end
604
605 function NamedSection.parse(self)
606 local s = self.section
607 local active = self:cfgvalue(s)
608
609 if self.addremove then
610 local path = self.config.."."..s
611 if active then -- Remove the section
612 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
613 return
614 end
615 else -- Create and apply default values
616 if luci.http.formvalue("cbi.cns."..path) then
617 self:create(s)
618 return
619 end
620 end
621 end
622
623 if active then
624 AbstractSection.parse_dynamic(self, s)
625 if luci.http.formvalue("cbi.submit") then
626 Node.parse(self, s)
627
628 if not self.override_scheme and self.map.scheme then
629 local co = self.map:get()
630 local stat, err = self.map.validator:validate_section(self.config, s, co)
631 if err then
632 --self.map.save = false
633 if err.code == luci.uvl.errors.ERR_DEPENDENCY then
634 self.tag_deperror[s] = true
635 else
636 self.tag_invalid[s] = true
637 end
638 for i, v in ipairs(err.childs) do
639 if v.option and self.fields[v.option] then
640 if v.code == luci.uvl.errors.ERR_DEPENDENCY then
641 self.fields[v.option].tag_reqerror[s] = true
642 elseif v.code == luci.uvl.errors.ERR_OPTION then
643 self.fields[v.option].tag_invalid[s] = true
644 end
645 end
646 end
647 end
648 end
649 end
650 AbstractSection.parse_optionals(self, s)
651 end
652 end
653
654
655 --[[
656 TypedSection - A (set of) configuration section(s) defined by the type
657 addremove: Defines whether the user can add/remove sections of this type
658 anonymous: Allow creating anonymous sections
659 validate: a validation function returning nil if the section is invalid
660 ]]--
661 TypedSection = class(AbstractSection)
662
663 function TypedSection.__init__(self, map, type, ...)
664 AbstractSection.__init__(self, map, type, ...)
665 Node._i18n(self, map.config, type, nil, ...)
666
667 self.template = "cbi/tsection"
668 self.deps = {}
669 self.anonymous = false
670
671 -- Use defaults from UVL
672 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
673 local vs = self.map:get_scheme(self.sectiontype)
674 self.addremove = not vs.unique and not vs.required
675 self.dynamic = vs.dynamic
676 self.anonymous = not vs.named
677 self.title = self.title or vs.title
678 self.description = self.description or vs.descr
679 end
680 end
681
682 -- Return all matching UCI sections for this TypedSection
683 function TypedSection.cfgsections(self)
684 local sections = {}
685 self.map.uci:foreach(self.map.config, self.sectiontype,
686 function (section)
687 if self:checkscope(section[".name"]) then
688 table.insert(sections, section[".name"])
689 end
690 end)
691
692 return sections
693 end
694
695 -- Limits scope to sections that have certain option => value pairs
696 function TypedSection.depends(self, option, value)
697 table.insert(self.deps, {option=option, value=value})
698 end
699
700 function TypedSection.parse(self)
701 if self.addremove then
702 -- Remove
703 local crval = REMOVE_PREFIX .. self.config
704 local name = luci.http.formvaluetable(crval)
705 for k,v in pairs(name) do
706 if self:cfgvalue(k) and self:checkscope(k) then
707 self:remove(k)
708 end
709 end
710 end
711
712 local co
713 for i, k in ipairs(self:cfgsections()) do
714 AbstractSection.parse_dynamic(self, k)
715 if luci.http.formvalue("cbi.submit") then
716 Node.parse(self, k)
717
718 if not self.override_scheme and self.map.scheme then
719 local co = self.map:get()
720 local stat, err = self.map.validator:validate_section(self.config, k, co)
721 if err then
722 --self.map.save = false
723 if err.code == luci.uvl.errors.ERR_DEPENDENCY then
724 self.tag_deperror[k] = true
725 else
726 self.tag_invalid[k] = true
727 end
728 for i, v in ipairs(err.childs) do
729 if v.option and self.fields[v.option] then
730 if v.code == luci.uvl.errors.ERR_DEPENDENCY then
731 self.fields[v.option].tag_reqerror[k] = true
732 elseif v.code == luci.uvl.errors.ERR_OPTION then
733 self.fields[v.option].tag_invalid[k] = true
734 end
735 end
736 end
737 end
738 end
739 end
740 AbstractSection.parse_optionals(self, k)
741 end
742
743 if self.addremove then
744 -- Create
745 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
746 local name = luci.http.formvalue(crval)
747 if self.anonymous then
748 if name then
749 self:create()
750 end
751 else
752 if name then
753 -- Ignore if it already exists
754 if self:cfgvalue(name) then
755 name = nil;
756 end
757
758 name = self:checkscope(name)
759
760 if not name then
761 self.err_invalid = true
762 end
763
764 if name and #name > 0 then
765 self:create(name)
766 end
767 end
768 end
769 end
770 end
771
772 -- Verifies scope of sections
773 function TypedSection.checkscope(self, section)
774 -- Check if we are not excluded
775 if self.filter and not self:filter(section) then
776 return nil
777 end
778
779 -- Check if at least one dependency is met
780 if #self.deps > 0 and self:cfgvalue(section) then
781 local stat = false
782
783 for k, v in ipairs(self.deps) do
784 if self:cfgvalue(section)[v.option] == v.value then
785 stat = true
786 end
787 end
788
789 if not stat then
790 return nil
791 end
792 end
793
794 return self:validate(section)
795 end
796
797
798 -- Dummy validate function
799 function TypedSection.validate(self, section)
800 return section
801 end
802
803
804 --[[
805 AbstractValue - An abstract Value Type
806 null: Value can be empty
807 valid: A function returning the value if it is valid otherwise nil
808 depends: A table of option => value pairs of which one must be true
809 default: The default value
810 size: The size of the input fields
811 rmempty: Unset value if empty
812 optional: This value is optional (see AbstractSection.optionals)
813 ]]--
814 AbstractValue = class(Node)
815
816 function AbstractValue.__init__(self, map, section, option, ...)
817 Node.__init__(self, ...)
818 self.section = section
819 self.option = option
820 self.map = map
821 self.config = map.config
822 self.tag_invalid = {}
823 self.tag_missing = {}
824 self.tag_reqerror = {}
825 self.tag_error = {}
826 self.deps = {}
827 self.cast = "string"
828
829 self.track_missing = false
830 self.rmempty = false
831 self.default = nil
832 self.size = nil
833 self.optional = false
834
835 -- Use defaults from UVL
836 if not self.override_scheme
837 and self.map:get_scheme(self.section.sectiontype, self.option) then
838 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
839 self.rmempty = not vs.required
840 self.cast = (vs.type == "list") and "list" or "string"
841 self.title = self.title or vs.title
842 self.description = self.description or vs.descr
843
844 if vs.depends and not self.override_dependencies then
845 for i, deps in ipairs(vs.depends) do
846 deps = _uvl_strip_remote_dependencies(deps)
847 if next(deps) then
848 self:depends(deps)
849 end
850 end
851 end
852 end
853 end
854
855 -- Add a dependencie to another section field
856 function AbstractValue.depends(self, field, value)
857 local deps
858 if type(field) == "string" then
859 deps = {}
860 deps[field] = value
861 else
862 deps = field
863 end
864
865 table.insert(self.deps, {deps=deps, add=""})
866 end
867
868 -- Generates the unique CBID
869 function AbstractValue.cbid(self, section)
870 return "cbid."..self.map.config.."."..section.."."..self.option
871 end
872
873 -- Return whether this object should be created
874 function AbstractValue.formcreated(self, section)
875 local key = "cbi.opt."..self.config.."."..section
876 return (luci.http.formvalue(key) == self.option)
877 end
878
879 -- Returns the formvalue for this object
880 function AbstractValue.formvalue(self, section)
881 return luci.http.formvalue(self:cbid(section))
882 end
883
884 function AbstractValue.additional(self, value)
885 self.optional = value
886 end
887
888 function AbstractValue.mandatory(self, value)
889 self.rmempty = not value
890 end
891
892 function AbstractValue.parse(self, section)
893 local fvalue = self:formvalue(section)
894 local cvalue = self:cfgvalue(section)
895
896 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
897 fvalue = self:transform(self:validate(fvalue, section))
898 if not fvalue then
899 self.tag_invalid[section] = true
900 end
901 if fvalue and not (fvalue == cvalue) then
902 self:write(section, fvalue)
903 end
904 else -- Unset the UCI or error
905 if self.rmempty or self.optional then
906 self:remove(section)
907 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
908 self.tag_missing[section] = true
909 end
910 end
911 end
912
913 -- Render if this value exists or if it is mandatory
914 function AbstractValue.render(self, s, scope)
915 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
916 scope = scope or {}
917 scope.section = s
918 scope.cbid = self:cbid(s)
919 scope.striptags = luci.util.striptags
920
921 scope.ifattr = function(cond,key,val)
922 if cond then
923 return string.format(
924 ' %s="%s"', tostring(key),
925 luci.util.pcdata(tostring( val
926 or scope[key]
927 or (type(self[key]) ~= "function" and self[key])
928 or "" ))
929 )
930 else
931 return ''
932 end
933 end
934
935 scope.attr = function(...)
936 return scope.ifattr( true, ... )
937 end
938
939 Node.render(self, scope)
940 end
941 end
942
943 -- Return the UCI value of this object
944 function AbstractValue.cfgvalue(self, section)
945 local value = self.map:get(section, self.option)
946 if not self.cast or self.cast == type(value) then
947 return value
948 elseif self.cast == "string" then
949 if type(value) == "table" then
950 return value[1]
951 end
952 elseif self.cast == "table" then
953 return {value}
954 end
955 end
956
957 -- Validate the form value
958 function AbstractValue.validate(self, value)
959 return value
960 end
961
962 AbstractValue.transform = AbstractValue.validate
963
964
965 -- Write to UCI
966 function AbstractValue.write(self, section, value)
967 return self.map:set(section, self.option, value)
968 end
969
970 -- Remove from UCI
971 function AbstractValue.remove(self, section)
972 return self.map:del(section, self.option)
973 end
974
975
976
977
978 --[[
979 Value - A one-line value
980 maxlength: The maximum length
981 ]]--
982 Value = class(AbstractValue)
983
984 function Value.__init__(self, ...)
985 AbstractValue.__init__(self, ...)
986 self.template = "cbi/value"
987 self.keylist = {}
988 self.vallist = {}
989 end
990
991 function Value.value(self, key, val)
992 val = val or key
993 table.insert(self.keylist, tostring(key))
994 table.insert(self.vallist, tostring(val))
995 end
996
997
998 -- DummyValue - This does nothing except being there
999 DummyValue = class(AbstractValue)
1000
1001 function DummyValue.__init__(self, ...)
1002 AbstractValue.__init__(self, ...)
1003 self.template = "cbi/dvalue"
1004 self.value = nil
1005 end
1006
1007 function DummyValue.parse(self)
1008
1009 end
1010
1011
1012 --[[
1013 Flag - A flag being enabled or disabled
1014 ]]--
1015 Flag = class(AbstractValue)
1016
1017 function Flag.__init__(self, ...)
1018 AbstractValue.__init__(self, ...)
1019 self.template = "cbi/fvalue"
1020
1021 self.enabled = "1"
1022 self.disabled = "0"
1023 end
1024
1025 -- A flag can only have two states: set or unset
1026 function Flag.parse(self, section)
1027 local fvalue = self:formvalue(section)
1028
1029 if fvalue then
1030 fvalue = self.enabled
1031 else
1032 fvalue = self.disabled
1033 end
1034
1035 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1036 if not(fvalue == self:cfgvalue(section)) then
1037 self:write(section, fvalue)
1038 end
1039 else
1040 self:remove(section)
1041 end
1042 end
1043
1044
1045
1046 --[[
1047 ListValue - A one-line value predefined in a list
1048 widget: The widget that will be used (select, radio)
1049 ]]--
1050 ListValue = class(AbstractValue)
1051
1052 function ListValue.__init__(self, ...)
1053 AbstractValue.__init__(self, ...)
1054 self.template = "cbi/lvalue"
1055
1056 self.keylist = {}
1057 self.vallist = {}
1058 self.size = 1
1059 self.widget = "select"
1060
1061 if not self.override_scheme
1062 and self.map:get_scheme(self.section.sectiontype, self.option) then
1063 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1064 if self.value and vs.values and not self.override_values then
1065 if self.rmempty or self.optional then
1066 self:value("")
1067 end
1068 for k, v in pairs(vs.values) do
1069 local deps = {}
1070 if not self.override_dependencies
1071 and vs.enum_depends and vs.enum_depends[k] then
1072 for i, dep in ipairs(vs.enum_depends[k]) do
1073 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1074 end
1075 end
1076 self:value(k, v, unpack(deps))
1077 end
1078 end
1079 end
1080 end
1081
1082 function ListValue.value(self, key, val, ...)
1083 if luci.util.contains(self.keylist, key) then
1084 return
1085 end
1086
1087 val = val or key
1088 table.insert(self.keylist, tostring(key))
1089 table.insert(self.vallist, tostring(val))
1090
1091 for i, deps in ipairs({...}) do
1092 table.insert(self.deps, {add = "-"..key, deps=deps})
1093 end
1094 end
1095
1096 function ListValue.validate(self, val)
1097 if luci.util.contains(self.keylist, val) then
1098 return val
1099 else
1100 return nil
1101 end
1102 end
1103
1104
1105
1106 --[[
1107 MultiValue - Multiple delimited values
1108 widget: The widget that will be used (select, checkbox)
1109 delimiter: The delimiter that will separate the values (default: " ")
1110 ]]--
1111 MultiValue = class(AbstractValue)
1112
1113 function MultiValue.__init__(self, ...)
1114 AbstractValue.__init__(self, ...)
1115 self.template = "cbi/mvalue"
1116
1117 self.keylist = {}
1118 self.vallist = {}
1119
1120 self.widget = "checkbox"
1121 self.delimiter = " "
1122 end
1123
1124 function MultiValue.render(self, ...)
1125 if self.widget == "select" and not self.size then
1126 self.size = #self.vallist
1127 end
1128
1129 AbstractValue.render(self, ...)
1130 end
1131
1132 function MultiValue.value(self, key, val)
1133 if luci.util.contains(self.keylist, key) then
1134 return
1135 end
1136
1137 val = val or key
1138 table.insert(self.keylist, tostring(key))
1139 table.insert(self.vallist, tostring(val))
1140 end
1141
1142 function MultiValue.valuelist(self, section)
1143 local val = self:cfgvalue(section)
1144
1145 if not(type(val) == "string") then
1146 return {}
1147 end
1148
1149 return luci.util.split(val, self.delimiter)
1150 end
1151
1152 function MultiValue.validate(self, val)
1153 val = (type(val) == "table") and val or {val}
1154
1155 local result
1156
1157 for i, value in ipairs(val) do
1158 if luci.util.contains(self.keylist, value) then
1159 result = result and (result .. self.delimiter .. value) or value
1160 end
1161 end
1162
1163 return result
1164 end
1165
1166
1167 StaticList = class(MultiValue)
1168
1169 function StaticList.__init__(self, ...)
1170 MultiValue.__init__(self, ...)
1171 self.cast = "table"
1172 self.valuelist = self.cfgvalue
1173
1174 if not self.override_scheme
1175 and self.map:get_scheme(self.section.sectiontype, self.option) then
1176 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1177 if self.value and vs.values and not self.override_values then
1178 for k, v in pairs(vs.values) do
1179 self:value(k, v)
1180 end
1181 end
1182 end
1183 end
1184
1185 function StaticList.validate(self, value)
1186 value = (type(value) == "table") and value or {value}
1187
1188 local valid = {}
1189 for i, v in ipairs(value) do
1190 if luci.util.contains(self.valuelist, v) then
1191 table.insert(valid, v)
1192 end
1193 end
1194 return valid
1195 end
1196
1197
1198 DynamicList = class(AbstractValue)
1199
1200 function DynamicList.__init__(self, ...)
1201 AbstractValue.__init__(self, ...)
1202 self.template = "cbi/dynlist"
1203 self.cast = "table"
1204 self.keylist = {}
1205 self.vallist = {}
1206 end
1207
1208 function DynamicList.value(self, key, val)
1209 val = val or key
1210 table.insert(self.keylist, tostring(key))
1211 table.insert(self.vallist, tostring(val))
1212 end
1213
1214 function DynamicList.validate(self, value, section)
1215 value = (type(value) == "table") and value or {value}
1216
1217 local valid = {}
1218 for i, v in ipairs(value) do
1219 if v and #v > 0 and
1220 not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1221 table.insert(valid, v)
1222 end
1223 end
1224
1225 return valid
1226 end
1227
1228
1229 --[[
1230 TextValue - A multi-line value
1231 rows: Rows
1232 ]]--
1233 TextValue = class(AbstractValue)
1234
1235 function TextValue.__init__(self, ...)
1236 AbstractValue.__init__(self, ...)
1237 self.template = "cbi/tvalue"
1238 end
1239
1240 --[[
1241 Button
1242 ]]--
1243 Button = class(AbstractValue)
1244
1245 function Button.__init__(self, ...)
1246 AbstractValue.__init__(self, ...)
1247 self.template = "cbi/button"
1248 self.inputstyle = nil
1249 self.rmempty = true
1250 end