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