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