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