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