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