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