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