2585e54590ac53c74456cde98463923d183dde67
[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 -- Create
703 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
704 local name = luci.http.formvalue(crval)
705 if self.anonymous then
706 if name then
707 self:create()
708 end
709 else
710 if name then
711 -- Ignore if it already exists
712 if self:cfgvalue(name) then
713 name = nil;
714 end
715
716 name = self:checkscope(name)
717
718 if not name then
719 self.err_invalid = true
720 end
721
722 if name and #name > 0 then
723 self:create(name)
724 end
725 end
726 end
727
728 -- Remove
729 crval = REMOVE_PREFIX .. self.config
730 name = luci.http.formvaluetable(crval)
731 for k,v in pairs(name) do
732 if self:cfgvalue(k) and self:checkscope(k) then
733 self:remove(k)
734 end
735 end
736 end
737
738 local co
739 for i, k in ipairs(self:cfgsections()) do
740 AbstractSection.parse_dynamic(self, k)
741 if luci.http.formvalue("cbi.submit") then
742 Node.parse(self, k)
743
744 if not self.override_scheme and self.map.scheme then
745 local co = self.map:get()
746 local stat, err = self.map.validator:validate_section(self.config, k, co)
747 if err then
748 self.map.save = false
749 if err.code == luci.uvl.errors.ERR_DEPENDENCY then
750 self.tag_deperror[k] = true
751 else
752 self.tag_invalid[k] = true
753 end
754 for i, v in ipairs(err.childs) do
755 if v.option and self.fields[v.option] then
756 if v.code == luci.uvl.errors.ERR_DEPENDENCY then
757 self.fields[v.option].tag_reqerror[k] = true
758 elseif v.code == luci.uvl.errors.ERR_OPTION then
759 self.fields[v.option].tag_invalid[k] = true
760 end
761 end
762 end
763 end
764 end
765 end
766 AbstractSection.parse_optionals(self, k)
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
842 if vs.depends and not self.override_dependencies then
843 for i, deps in ipairs(vs.depends) do
844 deps = _uvl_strip_remote_dependencies(deps)
845 if next(deps) then
846 self:depends(deps)
847 end
848 end
849 end
850 end
851 end
852
853 -- Add a dependencie to another section field
854 function AbstractValue.depends(self, field, value)
855 local deps
856 if type(field) == "string" then
857 deps = {}
858 deps[field] = value
859 else
860 deps = field
861 end
862
863 table.insert(self.deps, {deps=deps, add=""})
864 end
865
866 -- Generates the unique CBID
867 function AbstractValue.cbid(self, section)
868 return "cbid."..self.map.config.."."..section.."."..self.option
869 end
870
871 -- Return whether this object should be created
872 function AbstractValue.formcreated(self, section)
873 local key = "cbi.opt."..self.config.."."..section
874 return (luci.http.formvalue(key) == self.option)
875 end
876
877 -- Returns the formvalue for this object
878 function AbstractValue.formvalue(self, section)
879 return luci.http.formvalue(self:cbid(section))
880 end
881
882 function AbstractValue.additional(self, value)
883 self.optional = value
884 end
885
886 function AbstractValue.mandatory(self, value)
887 self.rmempty = not value
888 end
889
890 function AbstractValue.parse(self, section)
891 local fvalue = self:formvalue(section)
892 local cvalue = self:cfgvalue(section)
893
894 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
895 fvalue = self:transform(self:validate(fvalue, section))
896 if not fvalue then
897 self.tag_invalid[section] = true
898 end
899 if fvalue and not (fvalue == cvalue) then
900 self:write(section, fvalue)
901 end
902 else -- Unset the UCI or error
903 if self.rmempty or self.optional then
904 self:remove(section)
905 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
906 self.tag_missing[section] = true
907 end
908 end
909 end
910
911 -- Render if this value exists or if it is mandatory
912 function AbstractValue.render(self, s, scope)
913 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
914 scope = scope or {}
915 scope.section = s
916 scope.cbid = self:cbid(s)
917 scope.striptags = luci.util.striptags
918
919 scope.ifattr = function(cond,key,val)
920 if cond then
921 return string.format(
922 ' %s="%s"', tostring(key),
923 luci.util.pcdata(tostring( val
924 or scope[key]
925 or (type(self[key]) ~= "function" and self[key])
926 or "" ))
927 )
928 else
929 return ''
930 end
931 end
932
933 scope.attr = function(...)
934 return scope.ifattr( true, ... )
935 end
936
937 Node.render(self, scope)
938 end
939 end
940
941 -- Return the UCI value of this object
942 function AbstractValue.cfgvalue(self, section)
943 local value = self.map:get(section, self.option)
944 if not self.cast or self.cast == type(value) then
945 return value
946 elseif self.cast == "string" then
947 if type(value) == "table" then
948 return value[1]
949 end
950 elseif self.cast == "table" then
951 return {value}
952 end
953 end
954
955 -- Validate the form value
956 function AbstractValue.validate(self, value)
957 return value
958 end
959
960 AbstractValue.transform = AbstractValue.validate
961
962
963 -- Write to UCI
964 function AbstractValue.write(self, section, value)
965 return self.map:set(section, self.option, value)
966 end
967
968 -- Remove from UCI
969 function AbstractValue.remove(self, section)
970 return self.map:del(section, self.option)
971 end
972
973
974
975
976 --[[
977 Value - A one-line value
978 maxlength: The maximum length
979 ]]--
980 Value = class(AbstractValue)
981
982 function Value.__init__(self, ...)
983 AbstractValue.__init__(self, ...)
984 self.template = "cbi/value"
985 self.keylist = {}
986 self.vallist = {}
987 end
988
989 function Value.value(self, key, val)
990 val = val or key
991 table.insert(self.keylist, tostring(key))
992 table.insert(self.vallist, tostring(val))
993 end
994
995
996 -- DummyValue - This does nothing except being there
997 DummyValue = class(AbstractValue)
998
999 function DummyValue.__init__(self, ...)
1000 AbstractValue.__init__(self, ...)
1001 self.template = "cbi/dvalue"
1002 self.value = nil
1003 end
1004
1005 function DummyValue.parse(self)
1006
1007 end
1008
1009
1010 --[[
1011 Flag - A flag being enabled or disabled
1012 ]]--
1013 Flag = class(AbstractValue)
1014
1015 function Flag.__init__(self, ...)
1016 AbstractValue.__init__(self, ...)
1017 self.template = "cbi/fvalue"
1018
1019 self.enabled = "1"
1020 self.disabled = "0"
1021 end
1022
1023 -- A flag can only have two states: set or unset
1024 function Flag.parse(self, section)
1025 local fvalue = self:formvalue(section)
1026
1027 if fvalue then
1028 fvalue = self.enabled
1029 else
1030 fvalue = self.disabled
1031 end
1032
1033 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1034 if not(fvalue == self:cfgvalue(section)) then
1035 self:write(section, fvalue)
1036 end
1037 else
1038 self:remove(section)
1039 end
1040 end
1041
1042
1043
1044 --[[
1045 ListValue - A one-line value predefined in a list
1046 widget: The widget that will be used (select, radio)
1047 ]]--
1048 ListValue = class(AbstractValue)
1049
1050 function ListValue.__init__(self, ...)
1051 AbstractValue.__init__(self, ...)
1052 self.template = "cbi/lvalue"
1053
1054 self.keylist = {}
1055 self.vallist = {}
1056 self.size = 1
1057 self.widget = "select"
1058
1059 if not self.override_scheme
1060 and self.map:get_scheme(self.section.sectiontype, self.option) then
1061 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1062 if self.value and vs.values and not self.override_values then
1063 if self.rmempty or self.optional then
1064 self:value("")
1065 end
1066 for k, v in pairs(vs.values) do
1067 local deps = {}
1068 if not self.override_dependencies
1069 and vs.enum_depends and vs.enum_depends[k] then
1070 for i, dep in ipairs(vs.enum_depends[k]) do
1071 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1072 end
1073 end
1074 self:value(k, v, unpack(deps))
1075 end
1076 end
1077 end
1078 end
1079
1080 function ListValue.value(self, key, val, ...)
1081 if luci.util.contains(self.keylist, key) then
1082 return
1083 end
1084
1085 val = val or key
1086 table.insert(self.keylist, tostring(key))
1087 table.insert(self.vallist, tostring(val))
1088
1089 for i, deps in ipairs({...}) do
1090 table.insert(self.deps, {add = "-"..key, deps=deps})
1091 end
1092 end
1093
1094 function ListValue.validate(self, val)
1095 if luci.util.contains(self.keylist, val) then
1096 return val
1097 else
1098 return nil
1099 end
1100 end
1101
1102
1103
1104 --[[
1105 MultiValue - Multiple delimited values
1106 widget: The widget that will be used (select, checkbox)
1107 delimiter: The delimiter that will separate the values (default: " ")
1108 ]]--
1109 MultiValue = class(AbstractValue)
1110
1111 function MultiValue.__init__(self, ...)
1112 AbstractValue.__init__(self, ...)
1113 self.template = "cbi/mvalue"
1114
1115 self.keylist = {}
1116 self.vallist = {}
1117
1118 self.widget = "checkbox"
1119 self.delimiter = " "
1120 end
1121
1122 function MultiValue.render(self, ...)
1123 if self.widget == "select" and not self.size then
1124 self.size = #self.vallist
1125 end
1126
1127 AbstractValue.render(self, ...)
1128 end
1129
1130 function MultiValue.value(self, key, val)
1131 if luci.util.contains(self.keylist, key) then
1132 return
1133 end
1134
1135 val = val or key
1136 table.insert(self.keylist, tostring(key))
1137 table.insert(self.vallist, tostring(val))
1138 end
1139
1140 function MultiValue.valuelist(self, section)
1141 local val = self:cfgvalue(section)
1142
1143 if not(type(val) == "string") then
1144 return {}
1145 end
1146
1147 return luci.util.split(val, self.delimiter)
1148 end
1149
1150 function MultiValue.validate(self, val)
1151 val = (type(val) == "table") and val or {val}
1152
1153 local result
1154
1155 for i, value in ipairs(val) do
1156 if luci.util.contains(self.keylist, value) then
1157 result = result and (result .. self.delimiter .. value) or value
1158 end
1159 end
1160
1161 return result
1162 end
1163
1164
1165 StaticList = class(MultiValue)
1166
1167 function StaticList.__init__(self, ...)
1168 MultiValue.__init__(self, ...)
1169 self.cast = "table"
1170 self.valuelist = self.cfgvalue
1171
1172 if not self.override_scheme
1173 and self.map:get_scheme(self.section.sectiontype, self.option) then
1174 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1175 if self.value and vs.values and not self.override_values then
1176 for k, v in pairs(vs.values) do
1177 self:value(k, v)
1178 end
1179 end
1180 end
1181 end
1182
1183 function StaticList.validate(self, value)
1184 value = (type(value) == "table") and value or {value}
1185
1186 local valid = {}
1187 for i, v in ipairs(value) do
1188 if luci.util.contains(self.valuelist, v) then
1189 table.insert(valid, v)
1190 end
1191 end
1192 return valid
1193 end
1194
1195
1196 DynamicList = class(AbstractValue)
1197
1198 function DynamicList.__init__(self, ...)
1199 AbstractValue.__init__(self, ...)
1200 self.template = "cbi/dynlist"
1201 self.cast = "table"
1202 self.keylist = {}
1203 self.vallist = {}
1204 end
1205
1206 function DynamicList.value(self, key, val)
1207 val = val or key
1208 table.insert(self.keylist, tostring(key))
1209 table.insert(self.vallist, tostring(val))
1210 end
1211
1212 function DynamicList.validate(self, value, section)
1213 value = (type(value) == "table") and value or {value}
1214
1215 local valid = {}
1216 for i, v in ipairs(value) do
1217 if v and #v > 0 and
1218 not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1219 table.insert(valid, v)
1220 end
1221 end
1222
1223 return valid
1224 end
1225
1226
1227 --[[
1228 TextValue - A multi-line value
1229 rows: Rows
1230 ]]--
1231 TextValue = class(AbstractValue)
1232
1233 function TextValue.__init__(self, ...)
1234 AbstractValue.__init__(self, ...)
1235 self.template = "cbi/tvalue"
1236 end
1237
1238 --[[
1239 Button
1240 ]]--
1241 Button = class(AbstractValue)
1242
1243 function Button.__init__(self, ...)
1244 AbstractValue.__init__(self, ...)
1245 self.template = "cbi/button"
1246 self.inputstyle = nil
1247 self.rmempty = true
1248 end