Fixed DHCP-Options and some CBI stuff
[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, true)
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 = section:match("^%w+$") and 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, novld)
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 novld and 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, novld)
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 novld and 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 if not created then
799 self.invalid_cts = true
800 end
801 end
802 end
803 end
804
805 if created then
806 AbstractSection.parse_optionals(self, created)
807 end
808 end
809 end
810
811 -- Verifies scope of sections
812 function TypedSection.checkscope(self, section)
813 -- Check if we are not excluded
814 if self.filter and not self:filter(section) then
815 return nil
816 end
817
818 -- Check if at least one dependency is met
819 if #self.deps > 0 and self:cfgvalue(section) then
820 local stat = false
821
822 for k, v in ipairs(self.deps) do
823 if self:cfgvalue(section)[v.option] == v.value then
824 stat = true
825 end
826 end
827
828 if not stat then
829 return nil
830 end
831 end
832
833 return self:validate(section)
834 end
835
836
837 -- Dummy validate function
838 function TypedSection.validate(self, section)
839 return section
840 end
841
842
843 --[[
844 AbstractValue - An abstract Value Type
845 null: Value can be empty
846 valid: A function returning the value if it is valid otherwise nil
847 depends: A table of option => value pairs of which one must be true
848 default: The default value
849 size: The size of the input fields
850 rmempty: Unset value if empty
851 optional: This value is optional (see AbstractSection.optionals)
852 ]]--
853 AbstractValue = class(Node)
854
855 function AbstractValue.__init__(self, map, section, option, ...)
856 Node.__init__(self, ...)
857 self.section = section
858 self.option = option
859 self.map = map
860 self.config = map.config
861 self.tag_invalid = {}
862 self.tag_missing = {}
863 self.tag_reqerror = {}
864 self.tag_error = {}
865 self.deps = {}
866 self.cast = "string"
867
868 self.track_missing = false
869 self.rmempty = false
870 self.default = nil
871 self.size = nil
872 self.optional = false
873
874 -- Use defaults from UVL
875 if not self.override_scheme
876 and self.map:get_scheme(self.section.sectiontype, self.option) then
877 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
878 self.rmempty = not vs.required
879 self.cast = (vs.type == "list") and "list" or "string"
880 self.title = self.title or vs.title
881 self.description = self.description or vs.descr
882 self.default = vs.default
883
884 if vs.depends and not self.override_dependencies then
885 for i, deps in ipairs(vs.depends) do
886 deps = _uvl_strip_remote_dependencies(deps)
887 if next(deps) then
888 self:depends(deps)
889 end
890 end
891 end
892 end
893 end
894
895 -- Add a dependencie to another section field
896 function AbstractValue.depends(self, field, value)
897 local deps
898 if type(field) == "string" then
899 deps = {}
900 deps[field] = value
901 else
902 deps = field
903 end
904
905 table.insert(self.deps, {deps=deps, add=""})
906 end
907
908 -- Generates the unique CBID
909 function AbstractValue.cbid(self, section)
910 return "cbid."..self.map.config.."."..section.."."..self.option
911 end
912
913 -- Return whether this object should be created
914 function AbstractValue.formcreated(self, section)
915 local key = "cbi.opt."..self.config.."."..section
916 return (luci.http.formvalue(key) == self.option)
917 end
918
919 -- Returns the formvalue for this object
920 function AbstractValue.formvalue(self, section)
921 return luci.http.formvalue(self:cbid(section))
922 end
923
924 function AbstractValue.additional(self, value)
925 self.optional = value
926 end
927
928 function AbstractValue.mandatory(self, value)
929 self.rmempty = not value
930 end
931
932 function AbstractValue.parse(self, section)
933 local fvalue = self:formvalue(section)
934 local cvalue = self:cfgvalue(section)
935
936 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
937 fvalue = self:transform(self:validate(fvalue, section))
938 if not fvalue then
939 self.tag_invalid[section] = true
940 end
941 if fvalue and not (fvalue == cvalue) then
942 self:write(section, fvalue)
943 end
944 else -- Unset the UCI or error
945 if self.rmempty or self.optional then
946 self:remove(section)
947 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
948 self.tag_missing[section] = true
949 end
950 end
951 end
952
953 -- Render if this value exists or if it is mandatory
954 function AbstractValue.render(self, s, scope)
955 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
956 scope = scope or {}
957 scope.section = s
958 scope.cbid = self:cbid(s)
959 scope.striptags = luci.util.striptags
960
961 scope.ifattr = function(cond,key,val)
962 if cond then
963 return string.format(
964 ' %s="%s"', tostring(key),
965 luci.util.pcdata(tostring( val
966 or scope[key]
967 or (type(self[key]) ~= "function" and self[key])
968 or "" ))
969 )
970 else
971 return ''
972 end
973 end
974
975 scope.attr = function(...)
976 return scope.ifattr( true, ... )
977 end
978
979 Node.render(self, scope)
980 end
981 end
982
983 -- Return the UCI value of this object
984 function AbstractValue.cfgvalue(self, section)
985 local value = self.map:get(section, self.option)
986 if not value then
987 return nil
988 elseif not self.cast or self.cast == type(value) then
989 return value
990 elseif self.cast == "string" then
991 if type(value) == "table" then
992 return value[1]
993 end
994 elseif self.cast == "table" then
995 return {value}
996 end
997 end
998
999 -- Validate the form value
1000 function AbstractValue.validate(self, value)
1001 return value
1002 end
1003
1004 AbstractValue.transform = AbstractValue.validate
1005
1006
1007 -- Write to UCI
1008 function AbstractValue.write(self, section, value)
1009 return self.map:set(section, self.option, value)
1010 end
1011
1012 -- Remove from UCI
1013 function AbstractValue.remove(self, section)
1014 return self.map:del(section, self.option)
1015 end
1016
1017
1018
1019
1020 --[[
1021 Value - A one-line value
1022 maxlength: The maximum length
1023 ]]--
1024 Value = class(AbstractValue)
1025
1026 function Value.__init__(self, ...)
1027 AbstractValue.__init__(self, ...)
1028 self.template = "cbi/value"
1029 self.keylist = {}
1030 self.vallist = {}
1031 end
1032
1033 function Value.value(self, key, val)
1034 val = val or key
1035 table.insert(self.keylist, tostring(key))
1036 table.insert(self.vallist, tostring(val))
1037 end
1038
1039
1040 -- DummyValue - This does nothing except being there
1041 DummyValue = class(AbstractValue)
1042
1043 function DummyValue.__init__(self, ...)
1044 AbstractValue.__init__(self, ...)
1045 self.template = "cbi/dvalue"
1046 self.value = nil
1047 end
1048
1049 function DummyValue.parse(self)
1050
1051 end
1052
1053
1054 --[[
1055 Flag - A flag being enabled or disabled
1056 ]]--
1057 Flag = class(AbstractValue)
1058
1059 function Flag.__init__(self, ...)
1060 AbstractValue.__init__(self, ...)
1061 self.template = "cbi/fvalue"
1062
1063 self.enabled = "1"
1064 self.disabled = "0"
1065 end
1066
1067 -- A flag can only have two states: set or unset
1068 function Flag.parse(self, section)
1069 local fvalue = self:formvalue(section)
1070
1071 if fvalue then
1072 fvalue = self.enabled
1073 else
1074 fvalue = self.disabled
1075 end
1076
1077 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1078 if not(fvalue == self:cfgvalue(section)) then
1079 self:write(section, fvalue)
1080 end
1081 else
1082 self:remove(section)
1083 end
1084 end
1085
1086
1087
1088 --[[
1089 ListValue - A one-line value predefined in a list
1090 widget: The widget that will be used (select, radio)
1091 ]]--
1092 ListValue = class(AbstractValue)
1093
1094 function ListValue.__init__(self, ...)
1095 AbstractValue.__init__(self, ...)
1096 self.template = "cbi/lvalue"
1097
1098 self.keylist = {}
1099 self.vallist = {}
1100 self.size = 1
1101 self.widget = "select"
1102
1103 if not self.override_scheme
1104 and self.map:get_scheme(self.section.sectiontype, self.option) then
1105 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1106 if self.value and vs.values and not self.override_values then
1107 if self.rmempty or self.optional then
1108 self:value("")
1109 end
1110 for k, v in pairs(vs.values) do
1111 local deps = {}
1112 if not self.override_dependencies
1113 and vs.enum_depends and vs.enum_depends[k] then
1114 for i, dep in ipairs(vs.enum_depends[k]) do
1115 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1116 end
1117 end
1118 self:value(k, v, unpack(deps))
1119 end
1120 end
1121 end
1122 end
1123
1124 function ListValue.value(self, key, val, ...)
1125 if luci.util.contains(self.keylist, key) then
1126 return
1127 end
1128
1129 val = val or key
1130 table.insert(self.keylist, tostring(key))
1131 table.insert(self.vallist, tostring(val))
1132
1133 for i, deps in ipairs({...}) do
1134 table.insert(self.deps, {add = "-"..key, deps=deps})
1135 end
1136 end
1137
1138 function ListValue.validate(self, val)
1139 if luci.util.contains(self.keylist, val) then
1140 return val
1141 else
1142 return nil
1143 end
1144 end
1145
1146
1147
1148 --[[
1149 MultiValue - Multiple delimited values
1150 widget: The widget that will be used (select, checkbox)
1151 delimiter: The delimiter that will separate the values (default: " ")
1152 ]]--
1153 MultiValue = class(AbstractValue)
1154
1155 function MultiValue.__init__(self, ...)
1156 AbstractValue.__init__(self, ...)
1157 self.template = "cbi/mvalue"
1158
1159 self.keylist = {}
1160 self.vallist = {}
1161
1162 self.widget = "checkbox"
1163 self.delimiter = " "
1164 end
1165
1166 function MultiValue.render(self, ...)
1167 if self.widget == "select" and not self.size then
1168 self.size = #self.vallist
1169 end
1170
1171 AbstractValue.render(self, ...)
1172 end
1173
1174 function MultiValue.value(self, key, val)
1175 if luci.util.contains(self.keylist, key) then
1176 return
1177 end
1178
1179 val = val or key
1180 table.insert(self.keylist, tostring(key))
1181 table.insert(self.vallist, tostring(val))
1182 end
1183
1184 function MultiValue.valuelist(self, section)
1185 local val = self:cfgvalue(section)
1186
1187 if not(type(val) == "string") then
1188 return {}
1189 end
1190
1191 return luci.util.split(val, self.delimiter)
1192 end
1193
1194 function MultiValue.validate(self, val)
1195 val = (type(val) == "table") and val or {val}
1196
1197 local result
1198
1199 for i, value in ipairs(val) do
1200 if luci.util.contains(self.keylist, value) then
1201 result = result and (result .. self.delimiter .. value) or value
1202 end
1203 end
1204
1205 return result
1206 end
1207
1208
1209 StaticList = class(MultiValue)
1210
1211 function StaticList.__init__(self, ...)
1212 MultiValue.__init__(self, ...)
1213 self.cast = "table"
1214 self.valuelist = self.cfgvalue
1215
1216 if not self.override_scheme
1217 and self.map:get_scheme(self.section.sectiontype, self.option) then
1218 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1219 if self.value and vs.values and not self.override_values then
1220 for k, v in pairs(vs.values) do
1221 self:value(k, v)
1222 end
1223 end
1224 end
1225 end
1226
1227 function StaticList.validate(self, value)
1228 value = (type(value) == "table") and value or {value}
1229
1230 local valid = {}
1231 for i, v in ipairs(value) do
1232 if luci.util.contains(self.vallist, v) then
1233 table.insert(valid, v)
1234 end
1235 end
1236 return valid
1237 end
1238
1239
1240 DynamicList = class(AbstractValue)
1241
1242 function DynamicList.__init__(self, ...)
1243 AbstractValue.__init__(self, ...)
1244 self.template = "cbi/dynlist"
1245 self.cast = "table"
1246 self.keylist = {}
1247 self.vallist = {}
1248 end
1249
1250 function DynamicList.value(self, key, val)
1251 val = val or key
1252 table.insert(self.keylist, tostring(key))
1253 table.insert(self.vallist, tostring(val))
1254 end
1255
1256 function DynamicList.formvalue(self, section)
1257 local value = AbstractValue.formvalue(self, section)
1258 value = (type(value) == "table") and value or {value}
1259
1260 local valid = {}
1261 for i, v in ipairs(value) do
1262 if v and #v > 0
1263 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1264 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1265 table.insert(valid, v)
1266 end
1267 end
1268
1269 return valid
1270 end
1271
1272
1273 --[[
1274 TextValue - A multi-line value
1275 rows: Rows
1276 ]]--
1277 TextValue = class(AbstractValue)
1278
1279 function TextValue.__init__(self, ...)
1280 AbstractValue.__init__(self, ...)
1281 self.template = "cbi/tvalue"
1282 end
1283
1284 --[[
1285 Button
1286 ]]--
1287 Button = class(AbstractValue)
1288
1289 function Button.__init__(self, ...)
1290 AbstractValue.__init__(self, ...)
1291 self.template = "cbi/button"
1292 self.inputstyle = nil
1293 self.rmempty = true
1294 end