5b05c4608330dd9c3b7d3ee5b096b1e8d38e1073
[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 local function _uvl_validate_section(node, name)
82 local co = node.map:get()
83
84 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
85 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
86
87 local stat, err = node.map.validator:validate_section(node.config, name, co)
88 if err then
89 node.map.save = false
90 if err:is(luci.uvl.errors.ERR_DEPENDENCY) then
91 node.tag_deperror[name] = true
92 else
93 node.tag_invalid[name] = true
94 end
95 for i, v in ipairs(err.childs) do
96 if v.option and node.fields[v.option] then
97 if v:is(luci.uvl.errors.ERR_DEPENDENCY) then
98 node.fields[v.option].tag_reqerror[name] = true
99 elseif v:is(luci.uvl.errors.ERR_OPT_REQUIRED) then
100 node.fields[v.option].tag_missing[name] = true
101 node.tag_deperror[name] = true
102 elseif v:is(luci.uvl.errors.ERR_OPTION) then
103 node.fields[v.option].tag_invalid[name] = true
104 end
105 end
106 end
107 end
108
109 end
110
111 local function _uvl_strip_remote_dependencies(deps)
112 local clean = {}
113
114 for k, v in pairs(deps) do
115 k = k:gsub("%$config%.%$section%.", "")
116 if k:match("^[%w_]+$") and type(v) == "string" then
117 clean[k] = v
118 end
119 end
120
121 return clean
122 end
123
124
125 -- Node pseudo abstract class
126 Node = class()
127
128 function Node.__init__(self, title, description)
129 self.children = {}
130 self.title = title or ""
131 self.description = description or ""
132 self.template = "cbi/node"
133 end
134
135 -- i18n helper
136 function Node._i18n(self, config, section, option, title, description)
137
138 -- i18n loaded?
139 if type(luci.i18n) == "table" then
140
141 local key = config and config:gsub("[^%w]+", "") or ""
142
143 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
144 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
145
146 self.title = title or luci.i18n.translate( key, option or section or config )
147 self.description = description or luci.i18n.translate( key .. "_desc", "" )
148 end
149 end
150
151 -- Append child nodes
152 function Node.append(self, obj)
153 table.insert(self.children, obj)
154 end
155
156 -- Parse this node and its children
157 function Node.parse(self, ...)
158 for k, child in ipairs(self.children) do
159 child:parse(...)
160 end
161 end
162
163 -- Render this node
164 function Node.render(self, scope)
165 scope = scope or {}
166 scope.self = self
167
168 luci.template.render(self.template, scope)
169 end
170
171 -- Render the children
172 function Node.render_children(self, ...)
173 for k, node in ipairs(self.children) do
174 node:render(...)
175 end
176 end
177
178
179 --[[
180 A simple template element
181 ]]--
182 Template = class(Node)
183
184 function Template.__init__(self, template)
185 Node.__init__(self)
186 self.template = template
187 end
188
189 function Template.render(self)
190 luci.template.render(self.template, {self=self})
191 end
192
193
194 --[[
195 Map - A map describing a configuration file
196 ]]--
197 Map = class(Node)
198
199 function Map.__init__(self, config, ...)
200 Node.__init__(self, ...)
201 Node._i18n(self, config, nil, nil, ...)
202
203 self.config = config
204 self.parsechain = {self.config}
205 self.template = "cbi/map"
206 self.uci = uci.cursor()
207 self.save = true
208 if not self.uci:load(self.config) then
209 error("Unable to read UCI data: " .. self.config)
210 end
211
212 self.validator = luci.uvl.UVL()
213 self.scheme = self.validator:get_scheme(self.config)
214
215 end
216
217 function Map.get_scheme(self, sectiontype, option)
218 if not option then
219 return self.scheme and self.scheme.sections[sectiontype]
220 else
221 return self.scheme and self.scheme.variables[sectiontype]
222 and self.scheme.variables[sectiontype][option]
223 end
224 end
225
226
227 -- Chain foreign config
228 function Map.chain(self, config)
229 table.insert(self.parsechain, config)
230 end
231
232 -- Use optimized UCI writing
233 function Map.parse(self, ...)
234 Node.parse(self, ...)
235
236 if self.save then
237 for i, config in ipairs(self.parsechain) do
238 self.uci:save(config)
239 end
240 if luci.http.formvalue("cbi.apply") then
241 for i, config in ipairs(self.parsechain) do
242 self.uci:commit(config)
243 self.uci:apply(config)
244
245 -- Refresh data because commit changes section names
246 self.uci:load(config)
247 end
248
249 -- Reparse sections
250 Node.parse(self, ...)
251
252 end
253 for i, config in ipairs(self.parsechain) do
254 self.uci:unload(config)
255 end
256 end
257 end
258
259 -- Creates a child section
260 function Map.section(self, class, ...)
261 if instanceof(class, AbstractSection) then
262 local obj = class(self, ...)
263 self:append(obj)
264 return obj
265 else
266 error("class must be a descendent of AbstractSection")
267 end
268 end
269
270 -- UCI add
271 function Map.add(self, sectiontype)
272 return self.uci:add(self.config, sectiontype)
273 end
274
275 -- UCI set
276 function Map.set(self, section, option, value)
277 if option then
278 return self.uci:set(self.config, section, option, value)
279 else
280 return self.uci:set(self.config, section, value)
281 end
282 end
283
284 -- UCI del
285 function Map.del(self, section, option)
286 if option then
287 return self.uci:delete(self.config, section, option)
288 else
289 return self.uci:delete(self.config, section)
290 end
291 end
292
293 -- UCI get
294 function Map.get(self, section, option)
295 if not section then
296 return self.uci:get_all(self.config)
297 elseif option then
298 return self.uci:get(self.config, section, option)
299 else
300 return self.uci:get_all(self.config, section)
301 end
302 end
303
304
305 --[[
306 Page - A simple node
307 ]]--
308
309 Page = class(Node)
310 Page.__init__ = Node.__init__
311 Page.parse = function() end
312
313
314 --[[
315 SimpleForm - A Simple non-UCI form
316 ]]--
317 SimpleForm = class(Node)
318
319 function SimpleForm.__init__(self, config, title, description, data)
320 Node.__init__(self, title, description)
321 self.config = config
322 self.data = data or {}
323 self.template = "cbi/simpleform"
324 self.dorender = true
325 end
326
327 function SimpleForm.parse(self, ...)
328 if luci.http.formvalue("cbi.submit") then
329 Node.parse(self, 1, ...)
330 end
331
332 local valid = true
333 for k, j in ipairs(self.children) do
334 for i, v in ipairs(j.children) do
335 valid = valid
336 and (not v.tag_missing or not v.tag_missing[1])
337 and (not v.tag_invalid or not v.tag_invalid[1])
338 end
339 end
340
341 local state =
342 not luci.http.formvalue("cbi.submit") and 0
343 or valid and 1
344 or -1
345
346 self.dorender = not self.handle or self:handle(state, self.data) ~= false
347 end
348
349 function SimpleForm.render(self, ...)
350 if self.dorender then
351 Node.render(self, ...)
352 end
353 end
354
355 function SimpleForm.section(self, class, ...)
356 if instanceof(class, AbstractSection) then
357 local obj = class(self, ...)
358 self:append(obj)
359 return obj
360 else
361 error("class must be a descendent of AbstractSection")
362 end
363 end
364
365 -- Creates a child field
366 function SimpleForm.field(self, class, ...)
367 local section
368 for k, v in ipairs(self.children) do
369 if instanceof(v, SimpleSection) then
370 section = v
371 break
372 end
373 end
374 if not section then
375 section = self:section(SimpleSection)
376 end
377
378 if instanceof(class, AbstractValue) then
379 local obj = class(self, section, ...)
380 obj.track_missing = true
381 section:append(obj)
382 return obj
383 else
384 error("class must be a descendent of AbstractValue")
385 end
386 end
387
388 function SimpleForm.set(self, section, option, value)
389 self.data[option] = value
390 end
391
392
393 function SimpleForm.del(self, section, option)
394 self.data[option] = nil
395 end
396
397
398 function SimpleForm.get(self, section, option)
399 return self.data[option]
400 end
401
402
403 function SimpleForm.get_scheme()
404 return nil
405 end
406
407
408
409 --[[
410 AbstractSection
411 ]]--
412 AbstractSection = class(Node)
413
414 function AbstractSection.__init__(self, map, sectiontype, ...)
415 Node.__init__(self, ...)
416 self.sectiontype = sectiontype
417 self.map = map
418 self.config = map.config
419 self.optionals = {}
420 self.defaults = {}
421 self.fields = {}
422 self.tag_error = {}
423 self.tag_invalid = {}
424 self.tag_deperror = {}
425
426 self.optional = true
427 self.addremove = false
428 self.dynamic = false
429 end
430
431 -- Appends a new option
432 function AbstractSection.option(self, class, option, ...)
433 -- Autodetect from UVL
434 if class == true and self.map:get_scheme(self.sectiontype, option) then
435 local vs = self.map:get_scheme(self.sectiontype, option)
436 if vs.type == "boolean" then
437 class = Flag
438 elseif vs.type == "list" then
439 class = DynamicList
440 elseif vs.type == "enum" or vs.type == "reference" then
441 class = ListValue
442 else
443 class = Value
444 end
445 end
446
447 if instanceof(class, AbstractValue) then
448 local obj = class(self.map, self, option, ...)
449
450 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
451
452 self:append(obj)
453 self.fields[option] = obj
454 return obj
455 elseif class == true then
456 error("No valid class was given and autodetection failed.")
457 else
458 error("class must be a descendant of AbstractValue")
459 end
460 end
461
462 -- Parse optional options
463 function AbstractSection.parse_optionals(self, section)
464 if not self.optional then
465 return
466 end
467
468 self.optionals[section] = {}
469
470 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
471 for k,v in ipairs(self.children) do
472 if v.optional and not v:cfgvalue(section) then
473 if field == v.option then
474 field = nil
475 else
476 table.insert(self.optionals[section], v)
477 end
478 end
479 end
480
481 if field and #field > 0 and self.dynamic then
482 self:add_dynamic(field)
483 end
484 end
485
486 -- Add a dynamic option
487 function AbstractSection.add_dynamic(self, field, optional)
488 local o = self:option(Value, field, field)
489 o.optional = optional
490 end
491
492 -- Parse all dynamic options
493 function AbstractSection.parse_dynamic(self, section)
494 if not self.dynamic then
495 return
496 end
497
498 local arr = luci.util.clone(self:cfgvalue(section))
499 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
500 for k, v in pairs(form) do
501 arr[k] = v
502 end
503
504 for key,val in pairs(arr) do
505 local create = true
506
507 for i,c in ipairs(self.children) do
508 if c.option == key then
509 create = false
510 end
511 end
512
513 if create and key:sub(1, 1) ~= "." then
514 self:add_dynamic(key, true)
515 end
516 end
517 end
518
519 -- Returns the section's UCI table
520 function AbstractSection.cfgvalue(self, section)
521 return self.map:get(section)
522 end
523
524 -- Removes the section
525 function AbstractSection.remove(self, section)
526 return self.map:del(section)
527 end
528
529 -- Creates the section
530 function AbstractSection.create(self, section)
531 local stat
532
533 if section then
534 stat = self.map:set(section, nil, self.sectiontype)
535 else
536 section = self.map:add(self.sectiontype)
537 stat = section
538 end
539
540 if stat then
541 for k,v in pairs(self.children) do
542 if v.default then
543 self.map:set(section, v.option, v.default)
544 end
545 end
546
547 for k,v in pairs(self.defaults) do
548 self.map:set(section, k, v)
549 end
550 end
551
552 return stat
553 end
554
555
556 SimpleSection = class(AbstractSection)
557
558 function SimpleSection.__init__(self, form, ...)
559 AbstractSection.__init__(self, form, nil, ...)
560 self.template = "cbi/nullsection"
561 end
562
563
564 Table = class(AbstractSection)
565
566 function Table.__init__(self, form, data, ...)
567 local datasource = {}
568 datasource.config = "table"
569 self.data = data
570
571 function datasource.get(self, section, option)
572 return data[section] and data[section][option]
573 end
574
575 function datasource.del(...)
576 return true
577 end
578
579 function datasource.get_scheme()
580 return nil
581 end
582
583 AbstractSection.__init__(self, datasource, "table", ...)
584 self.template = "cbi/tblsection"
585 self.rowcolors = true
586 self.anonymous = true
587 end
588
589 function Table.parse(self)
590 for i, k in ipairs(self:cfgsections()) do
591 if luci.http.formvalue("cbi.submit") then
592 Node.parse(self, k)
593 end
594 end
595 end
596
597 function Table.cfgsections(self)
598 local sections = {}
599
600 for i, v in luci.util.kspairs(self.data) do
601 table.insert(sections, i)
602 end
603
604 return sections
605 end
606
607
608
609 --[[
610 NamedSection - A fixed configuration section defined by its name
611 ]]--
612 NamedSection = class(AbstractSection)
613
614 function NamedSection.__init__(self, map, section, stype, ...)
615 AbstractSection.__init__(self, map, stype, ...)
616 Node._i18n(self, map.config, section, nil, ...)
617
618 -- Defaults
619 self.addremove = false
620
621 -- Use defaults from UVL
622 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
623 local vs = self.map:get_scheme(self.sectiontype)
624 self.addremove = not vs.unique and not vs.required
625 self.dynamic = vs.dynamic
626 self.title = self.title or vs.title
627 self.description = self.description or vs.descr
628 end
629
630 self.template = "cbi/nsection"
631 self.section = section
632 end
633
634 function NamedSection.parse(self)
635 local s = self.section
636 local active = self:cfgvalue(s)
637
638 if self.addremove then
639 local path = self.config.."."..s
640 if active then -- Remove the section
641 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
642 return
643 end
644 else -- Create and apply default values
645 if luci.http.formvalue("cbi.cns."..path) then
646 self:create(s)
647 return
648 end
649 end
650 end
651
652 if active then
653 AbstractSection.parse_dynamic(self, s)
654 if luci.http.formvalue("cbi.submit") then
655 Node.parse(self, s)
656
657 if not self.override_scheme and self.map.scheme then
658 _uvl_validate_section(self, s)
659 end
660 end
661 AbstractSection.parse_optionals(self, s)
662 end
663 end
664
665
666 --[[
667 TypedSection - A (set of) configuration section(s) defined by the type
668 addremove: Defines whether the user can add/remove sections of this type
669 anonymous: Allow creating anonymous sections
670 validate: a validation function returning nil if the section is invalid
671 ]]--
672 TypedSection = class(AbstractSection)
673
674 function TypedSection.__init__(self, map, type, ...)
675 AbstractSection.__init__(self, map, type, ...)
676 Node._i18n(self, map.config, type, nil, ...)
677
678 self.template = "cbi/tsection"
679 self.deps = {}
680 self.anonymous = false
681
682 -- Use defaults from UVL
683 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
684 local vs = self.map:get_scheme(self.sectiontype)
685 self.addremove = not vs.unique and not vs.required
686 self.dynamic = vs.dynamic
687 self.anonymous = not vs.named
688 self.title = self.title or vs.title
689 self.description = self.description or vs.descr
690 end
691 end
692
693 -- Return all matching UCI sections for this TypedSection
694 function TypedSection.cfgsections(self)
695 local sections = {}
696 self.map.uci:foreach(self.map.config, self.sectiontype,
697 function (section)
698 if self:checkscope(section[".name"]) then
699 table.insert(sections, section[".name"])
700 end
701 end)
702
703 return sections
704 end
705
706 -- Limits scope to sections that have certain option => value pairs
707 function TypedSection.depends(self, option, value)
708 table.insert(self.deps, {option=option, value=value})
709 end
710
711 function TypedSection.parse(self)
712 if self.addremove then
713 -- Remove
714 local crval = REMOVE_PREFIX .. self.config
715 local name = luci.http.formvaluetable(crval)
716 for k,v in pairs(name) do
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
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