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