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