d2de8685b5303c9e8a56c039df8ac9e2829ea608
[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 if self.rmempty == nil then
890 self.rmempty = not vs.required
891 end
892 if self.cast == nil then
893 self.cast = (vs.type == "list") and "list" or "string"
894 end
895 self.title = self.title or vs.title
896 self.description = self.description or vs.descr
897 if self.default == nil then
898 self.default = vs.default
899 end
900
901 if vs.depends and not self.override_dependencies then
902 for i, deps in ipairs(vs.depends) do
903 deps = _uvl_strip_remote_dependencies(deps)
904 if next(deps) then
905 self:depends(deps)
906 end
907 end
908 end
909 end
910
911 self.cast = self.cast or "string"
912 end
913
914 -- Add a dependencie to another section field
915 function AbstractValue.depends(self, field, value)
916 local deps
917 if type(field) == "string" then
918 deps = {}
919 deps[field] = value
920 else
921 deps = field
922 end
923
924 table.insert(self.deps, {deps=deps, add=""})
925 end
926
927 -- Generates the unique CBID
928 function AbstractValue.cbid(self, section)
929 return "cbid."..self.map.config.."."..section.."."..self.option
930 end
931
932 -- Return whether this object should be created
933 function AbstractValue.formcreated(self, section)
934 local key = "cbi.opt."..self.config.."."..section
935 return (luci.http.formvalue(key) == self.option)
936 end
937
938 -- Returns the formvalue for this object
939 function AbstractValue.formvalue(self, section)
940 return luci.http.formvalue(self:cbid(section))
941 end
942
943 function AbstractValue.additional(self, value)
944 self.optional = value
945 end
946
947 function AbstractValue.mandatory(self, value)
948 self.rmempty = not value
949 end
950
951 function AbstractValue.parse(self, section)
952 local fvalue = self:formvalue(section)
953 local cvalue = self:cfgvalue(section)
954
955 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
956 fvalue = self:transform(self:validate(fvalue, section))
957 if not fvalue then
958 self.tag_invalid[section] = true
959 end
960 if fvalue and not (fvalue == cvalue) then
961 self:write(section, fvalue)
962 end
963 else -- Unset the UCI or error
964 if self.rmempty or self.optional then
965 self:remove(section)
966 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
967 self.tag_missing[section] = true
968 end
969 end
970 end
971
972 -- Render if this value exists or if it is mandatory
973 function AbstractValue.render(self, s, scope)
974 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
975 scope = scope or {}
976 scope.section = s
977 scope.cbid = self:cbid(s)
978 scope.striptags = luci.util.striptags
979
980 scope.ifattr = function(cond,key,val)
981 if cond then
982 return string.format(
983 ' %s="%s"', tostring(key),
984 luci.util.pcdata(tostring( val
985 or scope[key]
986 or (type(self[key]) ~= "function" and self[key])
987 or "" ))
988 )
989 else
990 return ''
991 end
992 end
993
994 scope.attr = function(...)
995 return scope.ifattr( true, ... )
996 end
997
998 Node.render(self, scope)
999 end
1000 end
1001
1002 -- Return the UCI value of this object
1003 function AbstractValue.cfgvalue(self, section)
1004 local value = self.map:get(section, self.option)
1005 if not value then
1006 return nil
1007 elseif not self.cast or self.cast == type(value) then
1008 return value
1009 elseif self.cast == "string" then
1010 if type(value) == "table" then
1011 return value[1]
1012 end
1013 elseif self.cast == "table" then
1014 return luci.util.split(value, "%s+", nil, true)
1015 end
1016 end
1017
1018 -- Validate the form value
1019 function AbstractValue.validate(self, value)
1020 return value
1021 end
1022
1023 AbstractValue.transform = AbstractValue.validate
1024
1025
1026 -- Write to UCI
1027 function AbstractValue.write(self, section, value)
1028 return self.map:set(section, self.option, value)
1029 end
1030
1031 -- Remove from UCI
1032 function AbstractValue.remove(self, section)
1033 return self.map:del(section, self.option)
1034 end
1035
1036
1037
1038
1039 --[[
1040 Value - A one-line value
1041 maxlength: The maximum length
1042 ]]--
1043 Value = class(AbstractValue)
1044
1045 function Value.__init__(self, ...)
1046 AbstractValue.__init__(self, ...)
1047 self.template = "cbi/value"
1048 self.keylist = {}
1049 self.vallist = {}
1050 end
1051
1052 function Value.value(self, key, val)
1053 val = val or key
1054 table.insert(self.keylist, tostring(key))
1055 table.insert(self.vallist, tostring(val))
1056 end
1057
1058
1059 -- DummyValue - This does nothing except being there
1060 DummyValue = class(AbstractValue)
1061
1062 function DummyValue.__init__(self, ...)
1063 AbstractValue.__init__(self, ...)
1064 self.template = "cbi/dvalue"
1065 self.value = nil
1066 end
1067
1068 function DummyValue.cfgvalue(self, section)
1069 local value
1070 if self.value then
1071 if type(self.value) == "function" then
1072 value = self:value(section)
1073 else
1074 value = self.value
1075 end
1076 else
1077 value = AbstractValue.cfgvalue(self, section)
1078 end
1079 return value
1080 end
1081
1082 function DummyValue.parse(self)
1083
1084 end
1085
1086
1087 --[[
1088 Flag - A flag being enabled or disabled
1089 ]]--
1090 Flag = class(AbstractValue)
1091
1092 function Flag.__init__(self, ...)
1093 AbstractValue.__init__(self, ...)
1094 self.template = "cbi/fvalue"
1095
1096 self.enabled = "1"
1097 self.disabled = "0"
1098 end
1099
1100 -- A flag can only have two states: set or unset
1101 function Flag.parse(self, section)
1102 local fvalue = self:formvalue(section)
1103
1104 if fvalue then
1105 fvalue = self.enabled
1106 else
1107 fvalue = self.disabled
1108 end
1109
1110 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1111 if not(fvalue == self:cfgvalue(section)) then
1112 self:write(section, fvalue)
1113 end
1114 else
1115 self:remove(section)
1116 end
1117 end
1118
1119
1120
1121 --[[
1122 ListValue - A one-line value predefined in a list
1123 widget: The widget that will be used (select, radio)
1124 ]]--
1125 ListValue = class(AbstractValue)
1126
1127 function ListValue.__init__(self, ...)
1128 AbstractValue.__init__(self, ...)
1129 self.template = "cbi/lvalue"
1130
1131 self.keylist = {}
1132 self.vallist = {}
1133 self.size = 1
1134 self.widget = "select"
1135 end
1136
1137 function ListValue.prepare(self, ...)
1138 AbstractValue.prepare(self, ...)
1139 if not self.override_scheme
1140 and self.map:get_scheme(self.section.sectiontype, self.option) then
1141 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1142 if self.value and vs.valuelist and not self.override_values then
1143 for k, v in ipairs(vs.valuelist) do
1144 local deps = {}
1145 if not self.override_dependencies
1146 and vs.enum_depends and vs.enum_depends[v.value] then
1147 for i, dep in ipairs(vs.enum_depends[v.value]) do
1148 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1149 end
1150 end
1151 self:value(v.value, v.title or v.value, unpack(deps))
1152 end
1153 end
1154 end
1155 end
1156
1157 function ListValue.value(self, key, val, ...)
1158 if luci.util.contains(self.keylist, key) then
1159 return
1160 end
1161
1162 val = val or key
1163 table.insert(self.keylist, tostring(key))
1164 table.insert(self.vallist, tostring(val))
1165
1166 for i, deps in ipairs({...}) do
1167 table.insert(self.deps, {add = "-"..key, deps=deps})
1168 end
1169 end
1170
1171 function ListValue.validate(self, val)
1172 if luci.util.contains(self.keylist, val) then
1173 return val
1174 else
1175 return nil
1176 end
1177 end
1178
1179
1180
1181 --[[
1182 MultiValue - Multiple delimited values
1183 widget: The widget that will be used (select, checkbox)
1184 delimiter: The delimiter that will separate the values (default: " ")
1185 ]]--
1186 MultiValue = class(AbstractValue)
1187
1188 function MultiValue.__init__(self, ...)
1189 AbstractValue.__init__(self, ...)
1190 self.template = "cbi/mvalue"
1191
1192 self.keylist = {}
1193 self.vallist = {}
1194
1195 self.widget = "checkbox"
1196 self.delimiter = " "
1197 end
1198
1199 function MultiValue.render(self, ...)
1200 if self.widget == "select" and not self.size then
1201 self.size = #self.vallist
1202 end
1203
1204 AbstractValue.render(self, ...)
1205 end
1206
1207 function MultiValue.value(self, key, val)
1208 if luci.util.contains(self.keylist, key) then
1209 return
1210 end
1211
1212 val = val or key
1213 table.insert(self.keylist, tostring(key))
1214 table.insert(self.vallist, tostring(val))
1215 end
1216
1217 function MultiValue.valuelist(self, section)
1218 local val = self:cfgvalue(section)
1219
1220 if not(type(val) == "string") then
1221 return {}
1222 end
1223
1224 return luci.util.split(val, self.delimiter)
1225 end
1226
1227 function MultiValue.validate(self, val)
1228 val = (type(val) == "table") and val or {val}
1229
1230 local result
1231
1232 for i, value in ipairs(val) do
1233 if luci.util.contains(self.keylist, value) then
1234 result = result and (result .. self.delimiter .. value) or value
1235 end
1236 end
1237
1238 return result
1239 end
1240
1241
1242 StaticList = class(MultiValue)
1243
1244 function StaticList.__init__(self, ...)
1245 MultiValue.__init__(self, ...)
1246 self.cast = "table"
1247 self.valuelist = self.cfgvalue
1248
1249 if not self.override_scheme
1250 and self.map:get_scheme(self.section.sectiontype, self.option) then
1251 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1252 if self.value and vs.values and not self.override_values then
1253 for k, v in pairs(vs.values) do
1254 self:value(k, v)
1255 end
1256 end
1257 end
1258 end
1259
1260 function StaticList.validate(self, value)
1261 value = (type(value) == "table") and value or {value}
1262
1263 local valid = {}
1264 for i, v in ipairs(value) do
1265 if luci.util.contains(self.vallist, v) then
1266 table.insert(valid, v)
1267 end
1268 end
1269 return valid
1270 end
1271
1272
1273 DynamicList = class(AbstractValue)
1274
1275 function DynamicList.__init__(self, ...)
1276 AbstractValue.__init__(self, ...)
1277 self.template = "cbi/dynlist"
1278 self.cast = "table"
1279 self.keylist = {}
1280 self.vallist = {}
1281 end
1282
1283 function DynamicList.value(self, key, val)
1284 val = val or key
1285 table.insert(self.keylist, tostring(key))
1286 table.insert(self.vallist, tostring(val))
1287 end
1288
1289 function DynamicList.formvalue(self, section)
1290 local value = AbstractValue.formvalue(self, section)
1291 value = (type(value) == "table") and value or {value}
1292
1293 local valid = {}
1294 for i, v in ipairs(value) do
1295 if v and #v > 0
1296 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1297 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1298 table.insert(valid, v)
1299 end
1300 end
1301
1302 return valid
1303 end
1304
1305
1306 --[[
1307 TextValue - A multi-line value
1308 rows: Rows
1309 ]]--
1310 TextValue = class(AbstractValue)
1311
1312 function TextValue.__init__(self, ...)
1313 AbstractValue.__init__(self, ...)
1314 self.template = "cbi/tvalue"
1315 end
1316
1317 --[[
1318 Button
1319 ]]--
1320 Button = class(AbstractValue)
1321
1322 function Button.__init__(self, ...)
1323 AbstractValue.__init__(self, ...)
1324 self.template = "cbi/button"
1325 self.inputstyle = nil
1326 self.rmempty = true
1327 end