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