Optimized UVL validation handling
[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 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
84 local stat, err = node.map.validator:validate_section(node.config, name, co)
85 if err then
86 node.map.save = false
87 if err.code == luci.uvl.errors.ERR_DEPENDENCY then
88 node.tag_deperror[name] = true
89 else
90 node.tag_invalid[name] = true
91 end
92 for i, v in ipairs(err.childs) do
93 if v.option and node.fields[v.option] then
94 if v.code == luci.uvl.errors.ERR_OPTION then
95 local subcode = v.childs and v.childs[1] and v.childs[1].code
96 if subcode == luci.uvl.errors.ERR_DEPENDENCY then
97 node.fields[v.option].tag_reqerror[name] = true
98 elseif subcode == luci.uvl.errors.ERR_OPT_REQUIRED then
99 node.fields[v.option].tag_missing[name] = true
100 node.tag_deperror[name] = true
101 else
102 node.fields[v.option].tag_invalid[name] = true
103 end
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 crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
739 local name = luci.http.formvalue(crval)
740 if self.anonymous then
741 if name then
742 self:create()
743 end
744 else
745 if name then
746 -- Ignore if it already exists
747 if self:cfgvalue(name) then
748 name = nil;
749 end
750
751 name = self:checkscope(name)
752
753 if not name then
754 self.err_invalid = true
755 end
756
757 if name and #name > 0 then
758 self:create(name)
759 end
760 end
761 end
762 end
763 end
764
765 -- Verifies scope of sections
766 function TypedSection.checkscope(self, section)
767 -- Check if we are not excluded
768 if self.filter and not self:filter(section) then
769 return nil
770 end
771
772 -- Check if at least one dependency is met
773 if #self.deps > 0 and self:cfgvalue(section) then
774 local stat = false
775
776 for k, v in ipairs(self.deps) do
777 if self:cfgvalue(section)[v.option] == v.value then
778 stat = true
779 end
780 end
781
782 if not stat then
783 return nil
784 end
785 end
786
787 return self:validate(section)
788 end
789
790
791 -- Dummy validate function
792 function TypedSection.validate(self, section)
793 return section
794 end
795
796
797 --[[
798 AbstractValue - An abstract Value Type
799 null: Value can be empty
800 valid: A function returning the value if it is valid otherwise nil
801 depends: A table of option => value pairs of which one must be true
802 default: The default value
803 size: The size of the input fields
804 rmempty: Unset value if empty
805 optional: This value is optional (see AbstractSection.optionals)
806 ]]--
807 AbstractValue = class(Node)
808
809 function AbstractValue.__init__(self, map, section, option, ...)
810 Node.__init__(self, ...)
811 self.section = section
812 self.option = option
813 self.map = map
814 self.config = map.config
815 self.tag_invalid = {}
816 self.tag_missing = {}
817 self.tag_reqerror = {}
818 self.tag_error = {}
819 self.deps = {}
820 self.cast = "string"
821
822 self.track_missing = false
823 self.rmempty = false
824 self.default = nil
825 self.size = nil
826 self.optional = false
827
828 -- Use defaults from UVL
829 if not self.override_scheme
830 and self.map:get_scheme(self.section.sectiontype, self.option) then
831 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
832 self.rmempty = not vs.required
833 self.cast = (vs.type == "list") and "list" or "string"
834 self.title = self.title or vs.title
835 self.description = self.description or vs.descr
836
837 if vs.depends and not self.override_dependencies then
838 for i, deps in ipairs(vs.depends) do
839 deps = _uvl_strip_remote_dependencies(deps)
840 if next(deps) then
841 self:depends(deps)
842 end
843 end
844 end
845 end
846 end
847
848 -- Add a dependencie to another section field
849 function AbstractValue.depends(self, field, value)
850 local deps
851 if type(field) == "string" then
852 deps = {}
853 deps[field] = value
854 else
855 deps = field
856 end
857
858 table.insert(self.deps, {deps=deps, add=""})
859 end
860
861 -- Generates the unique CBID
862 function AbstractValue.cbid(self, section)
863 return "cbid."..self.map.config.."."..section.."."..self.option
864 end
865
866 -- Return whether this object should be created
867 function AbstractValue.formcreated(self, section)
868 local key = "cbi.opt."..self.config.."."..section
869 return (luci.http.formvalue(key) == self.option)
870 end
871
872 -- Returns the formvalue for this object
873 function AbstractValue.formvalue(self, section)
874 return luci.http.formvalue(self:cbid(section))
875 end
876
877 function AbstractValue.additional(self, value)
878 self.optional = value
879 end
880
881 function AbstractValue.mandatory(self, value)
882 self.rmempty = not value
883 end
884
885 function AbstractValue.parse(self, section)
886 local fvalue = self:formvalue(section)
887 local cvalue = self:cfgvalue(section)
888
889 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
890 fvalue = self:transform(self:validate(fvalue, section))
891 if not fvalue then
892 self.tag_invalid[section] = true
893 end
894 if fvalue and not (fvalue == cvalue) then
895 self:write(section, fvalue)
896 end
897 else -- Unset the UCI or error
898 if self.rmempty or self.optional then
899 self:remove(section)
900 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
901 self.tag_missing[section] = true
902 end
903 end
904 end
905
906 -- Render if this value exists or if it is mandatory
907 function AbstractValue.render(self, s, scope)
908 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
909 scope = scope or {}
910 scope.section = s
911 scope.cbid = self:cbid(s)
912 scope.striptags = luci.util.striptags
913
914 scope.ifattr = function(cond,key,val)
915 if cond then
916 return string.format(
917 ' %s="%s"', tostring(key),
918 luci.util.pcdata(tostring( val
919 or scope[key]
920 or (type(self[key]) ~= "function" and self[key])
921 or "" ))
922 )
923 else
924 return ''
925 end
926 end
927
928 scope.attr = function(...)
929 return scope.ifattr( true, ... )
930 end
931
932 Node.render(self, scope)
933 end
934 end
935
936 -- Return the UCI value of this object
937 function AbstractValue.cfgvalue(self, section)
938 local value = self.map:get(section, self.option)
939 if not self.cast or self.cast == type(value) then
940 return value
941 elseif self.cast == "string" then
942 if type(value) == "table" then
943 return value[1]
944 end
945 elseif self.cast == "table" then
946 return {value}
947 end
948 end
949
950 -- Validate the form value
951 function AbstractValue.validate(self, value)
952 return value
953 end
954
955 AbstractValue.transform = AbstractValue.validate
956
957
958 -- Write to UCI
959 function AbstractValue.write(self, section, value)
960 return self.map:set(section, self.option, value)
961 end
962
963 -- Remove from UCI
964 function AbstractValue.remove(self, section)
965 return self.map:del(section, self.option)
966 end
967
968
969
970
971 --[[
972 Value - A one-line value
973 maxlength: The maximum length
974 ]]--
975 Value = class(AbstractValue)
976
977 function Value.__init__(self, ...)
978 AbstractValue.__init__(self, ...)
979 self.template = "cbi/value"
980 self.keylist = {}
981 self.vallist = {}
982 end
983
984 function Value.value(self, key, val)
985 val = val or key
986 table.insert(self.keylist, tostring(key))
987 table.insert(self.vallist, tostring(val))
988 end
989
990
991 -- DummyValue - This does nothing except being there
992 DummyValue = class(AbstractValue)
993
994 function DummyValue.__init__(self, ...)
995 AbstractValue.__init__(self, ...)
996 self.template = "cbi/dvalue"
997 self.value = nil
998 end
999
1000 function DummyValue.parse(self)
1001
1002 end
1003
1004
1005 --[[
1006 Flag - A flag being enabled or disabled
1007 ]]--
1008 Flag = class(AbstractValue)
1009
1010 function Flag.__init__(self, ...)
1011 AbstractValue.__init__(self, ...)
1012 self.template = "cbi/fvalue"
1013
1014 self.enabled = "1"
1015 self.disabled = "0"
1016 end
1017
1018 -- A flag can only have two states: set or unset
1019 function Flag.parse(self, section)
1020 local fvalue = self:formvalue(section)
1021
1022 if fvalue then
1023 fvalue = self.enabled
1024 else
1025 fvalue = self.disabled
1026 end
1027
1028 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1029 if not(fvalue == self:cfgvalue(section)) then
1030 self:write(section, fvalue)
1031 end
1032 else
1033 self:remove(section)
1034 end
1035 end
1036
1037
1038
1039 --[[
1040 ListValue - A one-line value predefined in a list
1041 widget: The widget that will be used (select, radio)
1042 ]]--
1043 ListValue = class(AbstractValue)
1044
1045 function ListValue.__init__(self, ...)
1046 AbstractValue.__init__(self, ...)
1047 self.template = "cbi/lvalue"
1048
1049 self.keylist = {}
1050 self.vallist = {}
1051 self.size = 1
1052 self.widget = "select"
1053
1054 if not self.override_scheme
1055 and self.map:get_scheme(self.section.sectiontype, self.option) then
1056 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1057 if self.value and vs.values and not self.override_values then
1058 if self.rmempty or self.optional then
1059 self:value("")
1060 end
1061 for k, v in pairs(vs.values) do
1062 local deps = {}
1063 if not self.override_dependencies
1064 and vs.enum_depends and vs.enum_depends[k] then
1065 for i, dep in ipairs(vs.enum_depends[k]) do
1066 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1067 end
1068 end
1069 self:value(k, v, unpack(deps))
1070 end
1071 end
1072 end
1073 end
1074
1075 function ListValue.value(self, key, val, ...)
1076 if luci.util.contains(self.keylist, key) then
1077 return
1078 end
1079
1080 val = val or key
1081 table.insert(self.keylist, tostring(key))
1082 table.insert(self.vallist, tostring(val))
1083
1084 for i, deps in ipairs({...}) do
1085 table.insert(self.deps, {add = "-"..key, deps=deps})
1086 end
1087 end
1088
1089 function ListValue.validate(self, val)
1090 if luci.util.contains(self.keylist, val) then
1091 return val
1092 else
1093 return nil
1094 end
1095 end
1096
1097
1098
1099 --[[
1100 MultiValue - Multiple delimited values
1101 widget: The widget that will be used (select, checkbox)
1102 delimiter: The delimiter that will separate the values (default: " ")
1103 ]]--
1104 MultiValue = class(AbstractValue)
1105
1106 function MultiValue.__init__(self, ...)
1107 AbstractValue.__init__(self, ...)
1108 self.template = "cbi/mvalue"
1109
1110 self.keylist = {}
1111 self.vallist = {}
1112
1113 self.widget = "checkbox"
1114 self.delimiter = " "
1115 end
1116
1117 function MultiValue.render(self, ...)
1118 if self.widget == "select" and not self.size then
1119 self.size = #self.vallist
1120 end
1121
1122 AbstractValue.render(self, ...)
1123 end
1124
1125 function MultiValue.value(self, key, val)
1126 if luci.util.contains(self.keylist, key) then
1127 return
1128 end
1129
1130 val = val or key
1131 table.insert(self.keylist, tostring(key))
1132 table.insert(self.vallist, tostring(val))
1133 end
1134
1135 function MultiValue.valuelist(self, section)
1136 local val = self:cfgvalue(section)
1137
1138 if not(type(val) == "string") then
1139 return {}
1140 end
1141
1142 return luci.util.split(val, self.delimiter)
1143 end
1144
1145 function MultiValue.validate(self, val)
1146 val = (type(val) == "table") and val or {val}
1147
1148 local result
1149
1150 for i, value in ipairs(val) do
1151 if luci.util.contains(self.keylist, value) then
1152 result = result and (result .. self.delimiter .. value) or value
1153 end
1154 end
1155
1156 return result
1157 end
1158
1159
1160 StaticList = class(MultiValue)
1161
1162 function StaticList.__init__(self, ...)
1163 MultiValue.__init__(self, ...)
1164 self.cast = "table"
1165 self.valuelist = self.cfgvalue
1166
1167 if not self.override_scheme
1168 and self.map:get_scheme(self.section.sectiontype, self.option) then
1169 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1170 if self.value and vs.values and not self.override_values then
1171 for k, v in pairs(vs.values) do
1172 self:value(k, v)
1173 end
1174 end
1175 end
1176 end
1177
1178 function StaticList.validate(self, value)
1179 value = (type(value) == "table") and value or {value}
1180
1181 local valid = {}
1182 for i, v in ipairs(value) do
1183 if luci.util.contains(self.valuelist, v) then
1184 table.insert(valid, v)
1185 end
1186 end
1187 return valid
1188 end
1189
1190
1191 DynamicList = class(AbstractValue)
1192
1193 function DynamicList.__init__(self, ...)
1194 AbstractValue.__init__(self, ...)
1195 self.template = "cbi/dynlist"
1196 self.cast = "table"
1197 self.keylist = {}
1198 self.vallist = {}
1199 end
1200
1201 function DynamicList.value(self, key, val)
1202 val = val or key
1203 table.insert(self.keylist, tostring(key))
1204 table.insert(self.vallist, tostring(val))
1205 end
1206
1207 function DynamicList.validate(self, value, section)
1208 value = (type(value) == "table") and value or {value}
1209
1210 local valid = {}
1211 for i, v in ipairs(value) do
1212 if v and #v > 0 and
1213 not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1214 table.insert(valid, v)
1215 end
1216 end
1217
1218 return valid
1219 end
1220
1221
1222 --[[
1223 TextValue - A multi-line value
1224 rows: Rows
1225 ]]--
1226 TextValue = class(AbstractValue)
1227
1228 function TextValue.__init__(self, ...)
1229 AbstractValue.__init__(self, ...)
1230 self.template = "cbi/tvalue"
1231 end
1232
1233 --[[
1234 Button
1235 ]]--
1236 Button = class(AbstractValue)
1237
1238 function Button.__init__(self, ...)
1239 AbstractValue.__init__(self, ...)
1240 self.template = "cbi/button"
1241 self.inputstyle = nil
1242 self.rmempty = true
1243 end