luci-0.9: merge r5382
[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 local util = require("luci.util")
31 require("luci.http")
32 require("luci.uvl")
33
34
35 --local event = require "luci.sys.event"
36 local fs = require("nixio.fs")
37 local uci = require("luci.model.uci")
38 local class = util.class
39 local instanceof = util.instanceof
40
41 FORM_NODATA = 0
42 FORM_PROCEED = 0
43 FORM_VALID = 1
44 FORM_DONE = 1
45 FORM_INVALID = -1
46 FORM_CHANGED = 2
47 FORM_SKIP = 4
48
49 AUTO = true
50
51 CREATE_PREFIX = "cbi.cts."
52 REMOVE_PREFIX = "cbi.rts."
53
54 -- Loads a CBI map from given file, creating an environment and returns it
55 function load(cbimap, ...)
56 local fs = require "nixio.fs"
57 local i18n = require "luci.i18n"
58 require("luci.config")
59 require("luci.util")
60
61 local upldir = "/lib/uci/upload/"
62 local cbidir = luci.util.libpath() .. "/model/cbi/"
63 local func, err
64
65 if fs.access(cbimap) then
66 func, err = loadfile(cbimap)
67 elseif fs.access(cbidir..cbimap..".lua") then
68 func, err = loadfile(cbidir..cbimap..".lua")
69 elseif fs.access(cbidir..cbimap..".lua.gz") then
70 func, err = loadfile(cbidir..cbimap..".lua.gz")
71 else
72 func, err = nil, "Model '" .. cbimap .. "' not found!"
73 end
74
75 assert(func, err)
76
77 luci.i18n.loadc("cbi")
78 luci.i18n.loadc("uvl")
79
80 local env = {
81 translate=i18n.translate,
82 translatef=i18n.translatef,
83 arg={...}
84 }
85
86 setfenv(func, setmetatable(env, {__index =
87 function(tbl, key)
88 return rawget(tbl, key) or _M[key] or _G[key]
89 end}))
90
91 local maps = { func() }
92 local uploads = { }
93 local has_upload = false
94
95 for i, map in ipairs(maps) do
96 if not instanceof(map, Node) then
97 error("CBI map returns no valid map object!")
98 return nil
99 else
100 map:prepare()
101 if map.upload_fields then
102 has_upload = true
103 for _, field in ipairs(map.upload_fields) do
104 uploads[
105 field.config .. '.' ..
106 field.section.sectiontype .. '.' ..
107 field.option
108 ] = true
109 end
110 end
111 end
112 end
113
114 if has_upload then
115 local uci = luci.model.uci.cursor()
116 local prm = luci.http.context.request.message.params
117 local fd, cbid
118
119 luci.http.setfilehandler(
120 function( field, chunk, eof )
121 if not field then return end
122 if field.name and not cbid then
123 local c, s, o = field.name:gmatch(
124 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
125 )()
126
127 if c and s and o then
128 local t = uci:get( c, s )
129 if t and uploads[c.."."..t.."."..o] then
130 local path = upldir .. field.name
131 fd = io.open(path, "w")
132 if fd then
133 cbid = field.name
134 prm[cbid] = path
135 end
136 end
137 end
138 end
139
140 if field.name == cbid and fd then
141 fd:write(chunk)
142 end
143
144 if eof and fd then
145 fd:close()
146 fd = nil
147 cbid = nil
148 end
149 end
150 )
151 end
152
153 return maps
154 end
155
156 local function _uvl_validate_section(node, name)
157 local co = node.map:get()
158
159 luci.uvl.STRICT_UNKNOWN_OPTIONS = false
160 luci.uvl.STRICT_UNKNOWN_SECTIONS = false
161
162 local function tag_fields(e)
163 if e.option and node.fields[e.option] then
164 if node.fields[e.option].error then
165 node.fields[e.option].error[name] = e
166 else
167 node.fields[e.option].error = { [name] = e }
168 end
169 elseif e.childs then
170 for _, c in ipairs(e.childs) do tag_fields(c) end
171 end
172 end
173
174 local function tag_section(e)
175 local s = { }
176 for _, c in ipairs(e.childs or { e }) do
177 if c.childs and not c:is(luci.uvl.errors.ERR_DEPENDENCY) then
178 table.insert( s, c.childs[1]:string() )
179 else
180 table.insert( s, c:string() )
181 end
182 end
183 if #s > 0 then
184 if node.error then
185 node.error[name] = s
186 else
187 node.error = { [name] = s }
188 end
189 end
190 end
191
192 local stat, err = node.map.validator:validate_section(node.config, name, co)
193 if err then
194 node.map.save = false
195 tag_fields(err)
196 tag_section(err)
197 end
198
199 end
200
201 local function _uvl_strip_remote_dependencies(deps)
202 local clean = {}
203
204 for k, v in pairs(deps) do
205 k = k:gsub("%$config%.%$section%.", "")
206 if k:match("^[%w_]+$") and type(v) == "string" then
207 clean[k] = v
208 end
209 end
210
211 return clean
212 end
213
214
215 -- Node pseudo abstract class
216 Node = class()
217
218 function Node.__init__(self, title, description)
219 self.children = {}
220 self.title = title or ""
221 self.description = description or ""
222 self.template = "cbi/node"
223 end
224
225 -- i18n helper
226 function Node._i18n(self, config, section, option, title, description)
227
228 -- i18n loaded?
229 if type(luci.i18n) == "table" then
230
231 local key = config and config:gsub("[^%w]+", "") or ""
232
233 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
234 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
235
236 self.title = title or luci.i18n.translate( key, option or section or config )
237 self.description = description or luci.i18n.translate( key .. "_desc", "" )
238 end
239 end
240
241 -- Prepare nodes
242 function Node.prepare(self, ...)
243 for k, child in ipairs(self.children) do
244 child:prepare(...)
245 end
246 end
247
248 -- Append child nodes
249 function Node.append(self, obj)
250 table.insert(self.children, obj)
251 end
252
253 -- Parse this node and its children
254 function Node.parse(self, ...)
255 for k, child in ipairs(self.children) do
256 child:parse(...)
257 end
258 end
259
260 -- Render this node
261 function Node.render(self, scope)
262 scope = scope or {}
263 scope.self = self
264
265 luci.template.render(self.template, scope)
266 end
267
268 -- Render the children
269 function Node.render_children(self, ...)
270 for k, node in ipairs(self.children) do
271 node:render(...)
272 end
273 end
274
275
276 --[[
277 A simple template element
278 ]]--
279 Template = class(Node)
280
281 function Template.__init__(self, template)
282 Node.__init__(self)
283 self.template = template
284 end
285
286 function Template.render(self)
287 luci.template.render(self.template, {self=self})
288 end
289
290 function Template.parse(self, readinput)
291 self.readinput = (readinput ~= false)
292 return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
293 end
294
295
296 --[[
297 Map - A map describing a configuration file
298 ]]--
299 Map = class(Node)
300
301 function Map.__init__(self, config, ...)
302 Node.__init__(self, ...)
303 Node._i18n(self, config, nil, nil, ...)
304
305 self.config = config
306 self.parsechain = {self.config}
307 self.template = "cbi/map"
308 self.apply_on_parse = nil
309 self.readinput = true
310 self.proceed = false
311 self.flow = {}
312
313 self.uci = uci.cursor()
314 self.save = true
315
316 self.changed = false
317
318 if not self.uci:load(self.config) then
319 error("Unable to read UCI data: " .. self.config)
320 end
321
322 self.validator = luci.uvl.UVL()
323 self.scheme = self.validator:get_scheme(self.config)
324
325 end
326
327 function Map.formvalue(self, key)
328 return self.readinput and luci.http.formvalue(key)
329 end
330
331 function Map.formvaluetable(self, key)
332 return self.readinput and luci.http.formvaluetable(key) or {}
333 end
334
335 function Map.get_scheme(self, sectiontype, option)
336 if not option then
337 return self.scheme and self.scheme.sections[sectiontype]
338 else
339 return self.scheme and self.scheme.variables[sectiontype]
340 and self.scheme.variables[sectiontype][option]
341 end
342 end
343
344 function Map.submitstate(self)
345 return self:formvalue("cbi.submit")
346 end
347
348 -- Chain foreign config
349 function Map.chain(self, config)
350 table.insert(self.parsechain, config)
351 end
352
353 function Map.state_handler(self, state)
354 return state
355 end
356
357 -- Use optimized UCI writing
358 function Map.parse(self, readinput, ...)
359 self.readinput = (readinput ~= false)
360
361 if self:formvalue("cbi.skip") then
362 self.state = FORM_SKIP
363 return self:state_handler(self.state)
364 end
365
366 Node.parse(self, ...)
367
368 if self.save then
369 for i, config in ipairs(self.parsechain) do
370 self.uci:save(config)
371 end
372 if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
373 for i, config in ipairs(self.parsechain) do
374 self.uci:commit(config)
375
376 -- Refresh data because commit changes section names
377 self.uci:load(config)
378 end
379 if self.apply_on_parse then
380 self.uci:apply(self.parsechain)
381 else
382 self._apply = function()
383 local cmd = self.uci:apply(self.parsechain, true)
384 return io.popen(cmd)
385 end
386 end
387
388 -- Reparse sections
389 Node.parse(self, true)
390
391 end
392 for i, config in ipairs(self.parsechain) do
393 self.uci:unload(config)
394 end
395 if type(self.commit_handler) == "function" then
396 self:commit_handler(self:submitstate())
397 end
398 end
399
400 if self:submitstate() then
401 if not self.save then
402 self.state = FORM_INVALID
403 elseif self.proceed then
404 self.state = FORM_PROCEED
405 else
406 self.state = self.changed and FORM_CHANGED or FORM_VALID
407 end
408 else
409 self.state = FORM_NODATA
410 end
411
412 return self:state_handler(self.state)
413 end
414
415 function Map.render(self, ...)
416 Node.render(self, ...)
417 if self._apply then
418 local fp = self._apply()
419 fp:read("*a")
420 fp:close()
421 end
422 end
423
424 -- Creates a child section
425 function Map.section(self, class, ...)
426 if instanceof(class, AbstractSection) then
427 local obj = class(self, ...)
428 self:append(obj)
429 return obj
430 else
431 error("class must be a descendent of AbstractSection")
432 end
433 end
434
435 -- UCI add
436 function Map.add(self, sectiontype)
437 return self.uci:add(self.config, sectiontype)
438 end
439
440 -- UCI set
441 function Map.set(self, section, option, value)
442 if option then
443 return self.uci:set(self.config, section, option, value)
444 else
445 return self.uci:set(self.config, section, value)
446 end
447 end
448
449 -- UCI del
450 function Map.del(self, section, option)
451 if option then
452 return self.uci:delete(self.config, section, option)
453 else
454 return self.uci:delete(self.config, section)
455 end
456 end
457
458 -- UCI get
459 function Map.get(self, section, option)
460 if not section then
461 return self.uci:get_all(self.config)
462 elseif option then
463 return self.uci:get(self.config, section, option)
464 else
465 return self.uci:get_all(self.config, section)
466 end
467 end
468
469 --[[
470 Compound - Container
471 ]]--
472 Compound = class(Node)
473
474 function Compound.__init__(self, ...)
475 Node.__init__(self)
476 self.template = "cbi/compound"
477 self.children = {...}
478 end
479
480 function Compound.populate_delegator(self, delegator)
481 for _, v in ipairs(self.children) do
482 v.delegator = delegator
483 end
484 end
485
486 function Compound.parse(self, ...)
487 local cstate, state = 0
488
489 for k, child in ipairs(self.children) do
490 cstate = child:parse(...)
491 state = (not state or cstate < state) and cstate or state
492 end
493
494 return state
495 end
496
497
498 --[[
499 Delegator - Node controller
500 ]]--
501 Delegator = class(Node)
502 function Delegator.__init__(self, ...)
503 Node.__init__(self, ...)
504 self.nodes = {}
505 self.defaultpath = {}
506 self.pageaction = false
507 self.readinput = true
508 self.allow_reset = false
509 self.allow_back = false
510 self.allow_finish = false
511 self.template = "cbi/delegator"
512 end
513
514 function Delegator.set(self, name, node)
515 if type(node) == "table" and getmetatable(node) == nil then
516 node = Compound(unpack(node))
517 end
518 assert(type(node) == "function" or instanceof(node, Compound), "Invalid")
519 assert(not self.nodes[name], "Duplicate entry")
520
521 self.nodes[name] = node
522 end
523
524 function Delegator.add(self, name, node)
525 node = self:set(name, node)
526 self.defaultpath[#self.defaultpath+1] = name
527 end
528
529 function Delegator.insert_after(self, name, after)
530 local n = #self.chain
531 for k, v in ipairs(self.chain) do
532 if v == state then
533 n = k + 1
534 break
535 end
536 end
537 table.insert(self.chain, n, name)
538 end
539
540 function Delegator.set_route(self, ...)
541 local n, chain, route = 0, self.chain, {...}
542 for i = 1, #chain do
543 if chain[i] == self.current then
544 n = i
545 break
546 end
547 end
548 for i = 1, #route do
549 n = n + 1
550 chain[n] = route[i]
551 end
552 for i = n + 1, #chain do
553 chain[i] = nil
554 end
555 end
556
557 function Delegator.get(self, name)
558 return self.nodes[name]
559 end
560
561 function Delegator.parse(self, ...)
562 local newcurrent
563 self.chain = self.chain or self:get_chain()
564 self.current = self.current or self:get_active()
565 self.active = self.active or self:get(self.current)
566 assert(self.active, "Invalid state")
567
568 local stat = FORM_DONE
569 if type(self.active) ~= "function" then
570 self.active:populate_delegator(self)
571 stat = self.active:parse()
572 else
573 self:active()
574 end
575
576 if stat > FORM_PROCEED then
577 if Map.formvalue(self, "cbi.delg.back") then
578 newcurrent = self:get_prev(self.current)
579 else
580 newcurrent = self:get_next(self.current)
581 end
582 elseif stat < FORM_PROCEED then
583 return stat
584 end
585
586
587 if not Map.formvalue(self, "cbi.submit") then
588 return FORM_NODATA
589 elseif not newcurrent or not self:get(newcurrent) then
590 return FORM_DONE
591 else
592 self.current = newcurrent
593 self.active = self:get(self.current)
594 if type(self.active) ~= "function" then
595 self.active:parse(false)
596 return FROM_PROCEED
597 else
598 return self:parse(...)
599 end
600 end
601 end
602
603 function Delegator.get_next(self, state)
604 for k, v in ipairs(self.chain) do
605 if v == state then
606 return self.chain[k+1]
607 end
608 end
609 end
610
611 function Delegator.get_prev(self, state)
612 for k, v in ipairs(self.chain) do
613 if v == state then
614 return self.chain[k-1]
615 end
616 end
617 end
618
619 function Delegator.get_chain(self)
620 local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
621 return type(x) == "table" and x or {x}
622 end
623
624 function Delegator.get_active(self)
625 return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
626 end
627
628 --[[
629 Page - A simple node
630 ]]--
631
632 Page = class(Node)
633 Page.__init__ = Node.__init__
634 Page.parse = function() end
635
636
637 --[[
638 SimpleForm - A Simple non-UCI form
639 ]]--
640 SimpleForm = class(Node)
641
642 function SimpleForm.__init__(self, config, title, description, data)
643 Node.__init__(self, title, description)
644 self.config = config
645 self.data = data or {}
646 self.template = "cbi/simpleform"
647 self.dorender = true
648 self.pageaction = false
649 self.readinput = true
650 end
651
652 SimpleForm.formvalue = Map.formvalue
653 SimpleForm.formvaluetable = Map.formvaluetable
654
655 function SimpleForm.parse(self, readinput, ...)
656 self.readinput = (readinput ~= false)
657
658 if self:formvalue("cbi.skip") then
659 return FORM_SKIP
660 end
661
662 if self:submitstate() then
663 Node.parse(self, 1, ...)
664 end
665
666 local valid = true
667 for k, j in ipairs(self.children) do
668 for i, v in ipairs(j.children) do
669 valid = valid
670 and (not v.tag_missing or not v.tag_missing[1])
671 and (not v.tag_invalid or not v.tag_invalid[1])
672 and (not v.error)
673 end
674 end
675
676 local state =
677 not self:submitstate() and FORM_NODATA
678 or valid and FORM_VALID
679 or FORM_INVALID
680
681 self.dorender = not self.handle
682 if self.handle then
683 local nrender, nstate = self:handle(state, self.data)
684 self.dorender = self.dorender or (nrender ~= false)
685 state = nstate or state
686 end
687 return state
688 end
689
690 function SimpleForm.render(self, ...)
691 if self.dorender then
692 Node.render(self, ...)
693 end
694 end
695
696 function SimpleForm.submitstate(self)
697 return self:formvalue("cbi.submit")
698 end
699
700 function SimpleForm.section(self, class, ...)
701 if instanceof(class, AbstractSection) then
702 local obj = class(self, ...)
703 self:append(obj)
704 return obj
705 else
706 error("class must be a descendent of AbstractSection")
707 end
708 end
709
710 -- Creates a child field
711 function SimpleForm.field(self, class, ...)
712 local section
713 for k, v in ipairs(self.children) do
714 if instanceof(v, SimpleSection) then
715 section = v
716 break
717 end
718 end
719 if not section then
720 section = self:section(SimpleSection)
721 end
722
723 if instanceof(class, AbstractValue) then
724 local obj = class(self, section, ...)
725 obj.track_missing = true
726 section:append(obj)
727 return obj
728 else
729 error("class must be a descendent of AbstractValue")
730 end
731 end
732
733 function SimpleForm.set(self, section, option, value)
734 self.data[option] = value
735 end
736
737
738 function SimpleForm.del(self, section, option)
739 self.data[option] = nil
740 end
741
742
743 function SimpleForm.get(self, section, option)
744 return self.data[option]
745 end
746
747
748 function SimpleForm.get_scheme()
749 return nil
750 end
751
752
753 Form = class(SimpleForm)
754
755 function Form.__init__(self, ...)
756 SimpleForm.__init__(self, ...)
757 self.embedded = true
758 end
759
760
761 --[[
762 AbstractSection
763 ]]--
764 AbstractSection = class(Node)
765
766 function AbstractSection.__init__(self, map, sectiontype, ...)
767 Node.__init__(self, ...)
768 self.sectiontype = sectiontype
769 self.map = map
770 self.config = map.config
771 self.optionals = {}
772 self.defaults = {}
773 self.fields = {}
774 self.tag_error = {}
775 self.tag_invalid = {}
776 self.tag_deperror = {}
777 self.changed = false
778
779 self.optional = true
780 self.addremove = false
781 self.dynamic = false
782 end
783
784 -- Appends a new option
785 function AbstractSection.option(self, class, option, ...)
786 -- Autodetect from UVL
787 if class == true and self.map:get_scheme(self.sectiontype, option) then
788 local vs = self.map:get_scheme(self.sectiontype, option)
789 if vs.type == "boolean" then
790 class = Flag
791 elseif vs.type == "list" then
792 class = DynamicList
793 elseif vs.type == "enum" or vs.type == "reference" then
794 class = ListValue
795 else
796 class = Value
797 end
798 end
799
800 if instanceof(class, AbstractValue) then
801 local obj = class(self.map, self, option, ...)
802
803 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
804
805 self:append(obj)
806 self.fields[option] = obj
807 return obj
808 elseif class == true then
809 error("No valid class was given and autodetection failed.")
810 else
811 error("class must be a descendant of AbstractValue")
812 end
813 end
814
815 -- Parse optional options
816 function AbstractSection.parse_optionals(self, section)
817 if not self.optional then
818 return
819 end
820
821 self.optionals[section] = {}
822
823 local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
824 for k,v in ipairs(self.children) do
825 if v.optional and not v:cfgvalue(section) then
826 if field == v.option then
827 field = nil
828 self.map.proceed = true
829 else
830 table.insert(self.optionals[section], v)
831 end
832 end
833 end
834
835 if field and #field > 0 and self.dynamic then
836 self:add_dynamic(field)
837 end
838 end
839
840 -- Add a dynamic option
841 function AbstractSection.add_dynamic(self, field, optional)
842 local o = self:option(Value, field, field)
843 o.optional = optional
844 end
845
846 -- Parse all dynamic options
847 function AbstractSection.parse_dynamic(self, section)
848 if not self.dynamic then
849 return
850 end
851
852 local arr = luci.util.clone(self:cfgvalue(section))
853 local form = self.map:formvaluetable("cbid."..self.config.."."..section)
854 for k, v in pairs(form) do
855 arr[k] = v
856 end
857
858 for key,val in pairs(arr) do
859 local create = true
860
861 for i,c in ipairs(self.children) do
862 if c.option == key then
863 create = false
864 end
865 end
866
867 if create and key:sub(1, 1) ~= "." then
868 self.map.proceed = true
869 self:add_dynamic(key, true)
870 end
871 end
872 end
873
874 -- Returns the section's UCI table
875 function AbstractSection.cfgvalue(self, section)
876 return self.map:get(section)
877 end
878
879 -- Push events
880 function AbstractSection.push_events(self)
881 --luci.util.append(self.map.events, self.events)
882 self.map.changed = true
883 end
884
885 -- Removes the section
886 function AbstractSection.remove(self, section)
887 self.map.proceed = true
888 return self.map:del(section)
889 end
890
891 -- Creates the section
892 function AbstractSection.create(self, section)
893 local stat
894
895 if section then
896 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
897 else
898 section = self.map:add(self.sectiontype)
899 stat = section
900 end
901
902 if stat then
903 for k,v in pairs(self.children) do
904 if v.default then
905 self.map:set(section, v.option, v.default)
906 end
907 end
908
909 for k,v in pairs(self.defaults) do
910 self.map:set(section, k, v)
911 end
912 end
913
914 self.map.proceed = true
915
916 return stat
917 end
918
919
920 SimpleSection = class(AbstractSection)
921
922 function SimpleSection.__init__(self, form, ...)
923 AbstractSection.__init__(self, form, nil, ...)
924 self.template = "cbi/nullsection"
925 end
926
927
928 Table = class(AbstractSection)
929
930 function Table.__init__(self, form, data, ...)
931 local datasource = {}
932 local tself = self
933 datasource.config = "table"
934 self.data = data or {}
935
936 datasource.formvalue = Map.formvalue
937 datasource.formvaluetable = Map.formvaluetable
938 datasource.readinput = true
939
940 function datasource.get(self, section, option)
941 return tself.data[section] and tself.data[section][option]
942 end
943
944 function datasource.submitstate(self)
945 return Map.formvalue(self, "cbi.submit")
946 end
947
948 function datasource.del(...)
949 return true
950 end
951
952 function datasource.get_scheme()
953 return nil
954 end
955
956 AbstractSection.__init__(self, datasource, "table", ...)
957 self.template = "cbi/tblsection"
958 self.rowcolors = true
959 self.anonymous = true
960 end
961
962 function Table.parse(self, readinput)
963 self.map.readinput = (readinput ~= false)
964 for i, k in ipairs(self:cfgsections()) do
965 if self.map:submitstate() then
966 Node.parse(self, k)
967 end
968 end
969 end
970
971 function Table.cfgsections(self)
972 local sections = {}
973
974 for i, v in luci.util.kspairs(self.data) do
975 table.insert(sections, i)
976 end
977
978 return sections
979 end
980
981 function Table.update(self, data)
982 self.data = data
983 end
984
985
986
987 --[[
988 NamedSection - A fixed configuration section defined by its name
989 ]]--
990 NamedSection = class(AbstractSection)
991
992 function NamedSection.__init__(self, map, section, stype, ...)
993 AbstractSection.__init__(self, map, stype, ...)
994 Node._i18n(self, map.config, section, nil, ...)
995
996 -- Defaults
997 self.addremove = false
998
999 -- Use defaults from UVL
1000 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1001 local vs = self.map:get_scheme(self.sectiontype)
1002 self.addremove = not vs.unique and not vs.required
1003 self.dynamic = vs.dynamic
1004 self.title = self.title or vs.title
1005 self.description = self.description or vs.descr
1006 end
1007
1008 self.template = "cbi/nsection"
1009 self.section = section
1010 end
1011
1012 function NamedSection.parse(self, novld)
1013 local s = self.section
1014 local active = self:cfgvalue(s)
1015
1016 if self.addremove then
1017 local path = self.config.."."..s
1018 if active then -- Remove the section
1019 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1020 self:push_events()
1021 return
1022 end
1023 else -- Create and apply default values
1024 if self.map:formvalue("cbi.cns."..path) then
1025 self:create(s)
1026 return
1027 end
1028 end
1029 end
1030
1031 if active then
1032 AbstractSection.parse_dynamic(self, s)
1033 if self.map:submitstate() then
1034 Node.parse(self, s)
1035
1036 if not novld and not self.override_scheme and self.map.scheme then
1037 _uvl_validate_section(self, s)
1038 end
1039 end
1040 AbstractSection.parse_optionals(self, s)
1041
1042 if self.changed then
1043 self:push_events()
1044 end
1045 end
1046 end
1047
1048
1049 --[[
1050 TypedSection - A (set of) configuration section(s) defined by the type
1051 addremove: Defines whether the user can add/remove sections of this type
1052 anonymous: Allow creating anonymous sections
1053 validate: a validation function returning nil if the section is invalid
1054 ]]--
1055 TypedSection = class(AbstractSection)
1056
1057 function TypedSection.__init__(self, map, type, ...)
1058 AbstractSection.__init__(self, map, type, ...)
1059 Node._i18n(self, map.config, type, nil, ...)
1060
1061 self.template = "cbi/tsection"
1062 self.deps = {}
1063 self.anonymous = false
1064
1065 -- Use defaults from UVL
1066 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
1067 local vs = self.map:get_scheme(self.sectiontype)
1068 self.addremove = not vs.unique and not vs.required
1069 self.dynamic = vs.dynamic
1070 self.anonymous = not vs.named
1071 self.title = self.title or vs.title
1072 self.description = self.description or vs.descr
1073 end
1074 end
1075
1076 -- Return all matching UCI sections for this TypedSection
1077 function TypedSection.cfgsections(self)
1078 local sections = {}
1079 self.map.uci:foreach(self.map.config, self.sectiontype,
1080 function (section)
1081 if self:checkscope(section[".name"]) then
1082 table.insert(sections, section[".name"])
1083 end
1084 end)
1085
1086 return sections
1087 end
1088
1089 -- Limits scope to sections that have certain option => value pairs
1090 function TypedSection.depends(self, option, value)
1091 table.insert(self.deps, {option=option, value=value})
1092 end
1093
1094 function TypedSection.parse(self, novld)
1095 if self.addremove then
1096 -- Remove
1097 local crval = REMOVE_PREFIX .. self.config
1098 local name = self.map:formvaluetable(crval)
1099 for k,v in pairs(name) do
1100 if k:sub(-2) == ".x" then
1101 k = k:sub(1, #k - 2)
1102 end
1103 if self:cfgvalue(k) and self:checkscope(k) then
1104 self:remove(k)
1105 end
1106 end
1107 end
1108
1109 local co
1110 for i, k in ipairs(self:cfgsections()) do
1111 AbstractSection.parse_dynamic(self, k)
1112 if self.map:submitstate() then
1113 Node.parse(self, k, novld)
1114
1115 if not novld and not self.override_scheme and self.map.scheme then
1116 _uvl_validate_section(self, k)
1117 end
1118 end
1119 AbstractSection.parse_optionals(self, k)
1120 end
1121
1122 if self.addremove then
1123 -- Create
1124 local created
1125 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1126 local name = self.map:formvalue(crval)
1127 if self.anonymous then
1128 if name then
1129 created = self:create()
1130 end
1131 else
1132 if name then
1133 -- Ignore if it already exists
1134 if self:cfgvalue(name) then
1135 name = nil;
1136 end
1137
1138 name = self:checkscope(name)
1139
1140 if not name then
1141 self.err_invalid = true
1142 end
1143
1144 if name and #name > 0 then
1145 created = self:create(name) and name
1146 if not created then
1147 self.invalid_cts = true
1148 end
1149 end
1150 end
1151 end
1152
1153 if created then
1154 AbstractSection.parse_optionals(self, created)
1155 end
1156 end
1157
1158 if created or self.changed then
1159 self:push_events()
1160 end
1161 end
1162
1163 -- Verifies scope of sections
1164 function TypedSection.checkscope(self, section)
1165 -- Check if we are not excluded
1166 if self.filter and not self:filter(section) then
1167 return nil
1168 end
1169
1170 -- Check if at least one dependency is met
1171 if #self.deps > 0 and self:cfgvalue(section) then
1172 local stat = false
1173
1174 for k, v in ipairs(self.deps) do
1175 if self:cfgvalue(section)[v.option] == v.value then
1176 stat = true
1177 end
1178 end
1179
1180 if not stat then
1181 return nil
1182 end
1183 end
1184
1185 return self:validate(section)
1186 end
1187
1188
1189 -- Dummy validate function
1190 function TypedSection.validate(self, section)
1191 return section
1192 end
1193
1194
1195 --[[
1196 AbstractValue - An abstract Value Type
1197 null: Value can be empty
1198 valid: A function returning the value if it is valid otherwise nil
1199 depends: A table of option => value pairs of which one must be true
1200 default: The default value
1201 size: The size of the input fields
1202 rmempty: Unset value if empty
1203 optional: This value is optional (see AbstractSection.optionals)
1204 ]]--
1205 AbstractValue = class(Node)
1206
1207 function AbstractValue.__init__(self, map, section, option, ...)
1208 Node.__init__(self, ...)
1209 self.section = section
1210 self.option = option
1211 self.map = map
1212 self.config = map.config
1213 self.tag_invalid = {}
1214 self.tag_missing = {}
1215 self.tag_reqerror = {}
1216 self.tag_error = {}
1217 self.deps = {}
1218 --self.cast = "string"
1219
1220 self.track_missing = false
1221 self.rmempty = true
1222 self.default = nil
1223 self.size = nil
1224 self.optional = false
1225 end
1226
1227 function AbstractValue.prepare(self)
1228 -- Use defaults from UVL
1229 if not self.override_scheme
1230 and self.map:get_scheme(self.section.sectiontype, self.option) then
1231 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1232 if self.cast == nil then
1233 self.cast = (vs.type == "list") and "list" or "string"
1234 end
1235 self.title = self.title or vs.title
1236 self.description = self.description or vs.descr
1237 if self.default == nil then
1238 self.default = vs.default
1239 end
1240
1241 if vs.depends and not self.override_dependencies then
1242 for i, deps in ipairs(vs.depends) do
1243 deps = _uvl_strip_remote_dependencies(deps)
1244 if next(deps) then
1245 self:depends(deps)
1246 end
1247 end
1248 end
1249 end
1250
1251 self.cast = self.cast or "string"
1252 end
1253
1254 -- Add a dependencie to another section field
1255 function AbstractValue.depends(self, field, value)
1256 local deps
1257 if type(field) == "string" then
1258 deps = {}
1259 deps[field] = value
1260 else
1261 deps = field
1262 end
1263
1264 table.insert(self.deps, {deps=deps, add=""})
1265 end
1266
1267 -- Generates the unique CBID
1268 function AbstractValue.cbid(self, section)
1269 return "cbid."..self.map.config.."."..section.."."..self.option
1270 end
1271
1272 -- Return whether this object should be created
1273 function AbstractValue.formcreated(self, section)
1274 local key = "cbi.opt."..self.config.."."..section
1275 return (self.map:formvalue(key) == self.option)
1276 end
1277
1278 -- Returns the formvalue for this object
1279 function AbstractValue.formvalue(self, section)
1280 return self.map:formvalue(self:cbid(section))
1281 end
1282
1283 function AbstractValue.additional(self, value)
1284 self.optional = value
1285 end
1286
1287 function AbstractValue.mandatory(self, value)
1288 self.rmempty = not value
1289 end
1290
1291 function AbstractValue.parse(self, section, novld)
1292 local fvalue = self:formvalue(section)
1293 local cvalue = self:cfgvalue(section)
1294
1295 -- If favlue and cvalue are both tables and have the same content
1296 -- make them identical
1297 if type(fvalue) == "table" and type(cvalue) == "table" then
1298 local equal = #fvalue == #cvalue
1299 if equal then
1300 for i=1, #fvalue do
1301 if cvalue[i] ~= fvalue[i] then
1302 equal = false
1303 end
1304 end
1305 end
1306 if equal then
1307 fvalue = cvalue
1308 end
1309 end
1310
1311 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1312 fvalue = self:transform(self:validate(fvalue, section))
1313 if not fvalue and not novld then
1314 if self.error then
1315 self.error[section] = "invalid"
1316 else
1317 self.error = { [section] = "invalid" }
1318 end
1319 if self.section.error then
1320 table.insert(self.section.error[section], "invalid")
1321 else
1322 self.section.error = {[section] = {"invalid"}}
1323 end
1324 self.map.save = false
1325 end
1326 if fvalue and not (fvalue == cvalue) then
1327 if self:write(section, fvalue) then
1328 -- Push events
1329 self.section.changed = true
1330 --luci.util.append(self.map.events, self.events)
1331 end
1332 end
1333 else -- Unset the UCI or error
1334 if self.rmempty or self.optional then
1335 if self:remove(section) then
1336 -- Push events
1337 self.section.changed = true
1338 --luci.util.append(self.map.events, self.events)
1339 end
1340 elseif cvalue ~= fvalue and not novld then
1341 self:write(section, fvalue or "")
1342 if self.error then
1343 self.error[section] = "missing"
1344 else
1345 self.error = { [section] = "missing" }
1346 end
1347 self.map.save = false
1348 end
1349 end
1350 end
1351
1352 -- Render if this value exists or if it is mandatory
1353 function AbstractValue.render(self, s, scope)
1354 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1355 scope = scope or {}
1356 scope.section = s
1357 scope.cbid = self:cbid(s)
1358 scope.striptags = luci.util.striptags
1359
1360 scope.ifattr = function(cond,key,val)
1361 if cond then
1362 return string.format(
1363 ' %s="%s"', tostring(key),
1364 luci.util.pcdata(tostring( val
1365 or scope[key]
1366 or (type(self[key]) ~= "function" and self[key])
1367 or "" ))
1368 )
1369 else
1370 return ''
1371 end
1372 end
1373
1374 scope.attr = function(...)
1375 return scope.ifattr( true, ... )
1376 end
1377
1378 Node.render(self, scope)
1379 end
1380 end
1381
1382 -- Return the UCI value of this object
1383 function AbstractValue.cfgvalue(self, section)
1384 local value = self.map:get(section, self.option)
1385 if not value then
1386 return nil
1387 elseif not self.cast or self.cast == type(value) then
1388 return value
1389 elseif self.cast == "string" then
1390 if type(value) == "table" then
1391 return value[1]
1392 end
1393 elseif self.cast == "table" then
1394 return luci.util.split(value, "%s+", nil, true)
1395 end
1396 end
1397
1398 -- Validate the form value
1399 function AbstractValue.validate(self, value)
1400 return value
1401 end
1402
1403 AbstractValue.transform = AbstractValue.validate
1404
1405
1406 -- Write to UCI
1407 function AbstractValue.write(self, section, value)
1408 return self.map:set(section, self.option, value)
1409 end
1410
1411 -- Remove from UCI
1412 function AbstractValue.remove(self, section)
1413 return self.map:del(section, self.option)
1414 end
1415
1416
1417
1418
1419 --[[
1420 Value - A one-line value
1421 maxlength: The maximum length
1422 ]]--
1423 Value = class(AbstractValue)
1424
1425 function Value.__init__(self, ...)
1426 AbstractValue.__init__(self, ...)
1427 self.template = "cbi/value"
1428 self.keylist = {}
1429 self.vallist = {}
1430 end
1431
1432 function Value.value(self, key, val)
1433 val = val or key
1434 table.insert(self.keylist, tostring(key))
1435 table.insert(self.vallist, tostring(val))
1436 end
1437
1438
1439 -- DummyValue - This does nothing except being there
1440 DummyValue = class(AbstractValue)
1441
1442 function DummyValue.__init__(self, ...)
1443 AbstractValue.__init__(self, ...)
1444 self.template = "cbi/dvalue"
1445 self.value = nil
1446 end
1447
1448 function DummyValue.cfgvalue(self, section)
1449 local value
1450 if self.value then
1451 if type(self.value) == "function" then
1452 value = self:value(section)
1453 else
1454 value = self.value
1455 end
1456 else
1457 value = AbstractValue.cfgvalue(self, section)
1458 end
1459 return value
1460 end
1461
1462 function DummyValue.parse(self)
1463
1464 end
1465
1466
1467 --[[
1468 Flag - A flag being enabled or disabled
1469 ]]--
1470 Flag = class(AbstractValue)
1471
1472 function Flag.__init__(self, ...)
1473 AbstractValue.__init__(self, ...)
1474 self.template = "cbi/fvalue"
1475
1476 self.enabled = "1"
1477 self.disabled = "0"
1478 end
1479
1480 -- A flag can only have two states: set or unset
1481 function Flag.parse(self, section)
1482 local fvalue = self:formvalue(section)
1483
1484 if fvalue then
1485 fvalue = self.enabled
1486 else
1487 fvalue = self.disabled
1488 end
1489
1490 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1491 if not(fvalue == self:cfgvalue(section)) then
1492 self:write(section, fvalue)
1493 end
1494 else
1495 self:remove(section)
1496 end
1497 end
1498
1499
1500
1501 --[[
1502 ListValue - A one-line value predefined in a list
1503 widget: The widget that will be used (select, radio)
1504 ]]--
1505 ListValue = class(AbstractValue)
1506
1507 function ListValue.__init__(self, ...)
1508 AbstractValue.__init__(self, ...)
1509 self.template = "cbi/lvalue"
1510
1511 self.keylist = {}
1512 self.vallist = {}
1513 self.size = 1
1514 self.widget = "select"
1515 end
1516
1517 function ListValue.prepare(self, ...)
1518 AbstractValue.prepare(self, ...)
1519 if not self.override_scheme
1520 and self.map:get_scheme(self.section.sectiontype, self.option) then
1521 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1522 if self.value and vs.valuelist and not self.override_values then
1523 for k, v in ipairs(vs.valuelist) do
1524 local deps = {}
1525 if not self.override_dependencies
1526 and vs.enum_depends and vs.enum_depends[v.value] then
1527 for i, dep in ipairs(vs.enum_depends[v.value]) do
1528 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1529 end
1530 end
1531 self:value(v.value, v.title or v.value, unpack(deps))
1532 end
1533 end
1534 end
1535 end
1536
1537 function ListValue.value(self, key, val, ...)
1538 if luci.util.contains(self.keylist, key) then
1539 return
1540 end
1541
1542 val = val or key
1543 table.insert(self.keylist, tostring(key))
1544 table.insert(self.vallist, tostring(val))
1545
1546 for i, deps in ipairs({...}) do
1547 table.insert(self.deps, {add = "-"..key, deps=deps})
1548 end
1549 end
1550
1551 function ListValue.validate(self, val)
1552 if luci.util.contains(self.keylist, val) then
1553 return val
1554 else
1555 return nil
1556 end
1557 end
1558
1559
1560
1561 --[[
1562 MultiValue - Multiple delimited values
1563 widget: The widget that will be used (select, checkbox)
1564 delimiter: The delimiter that will separate the values (default: " ")
1565 ]]--
1566 MultiValue = class(AbstractValue)
1567
1568 function MultiValue.__init__(self, ...)
1569 AbstractValue.__init__(self, ...)
1570 self.template = "cbi/mvalue"
1571
1572 self.keylist = {}
1573 self.vallist = {}
1574
1575 self.widget = "checkbox"
1576 self.delimiter = " "
1577 end
1578
1579 function MultiValue.render(self, ...)
1580 if self.widget == "select" and not self.size then
1581 self.size = #self.vallist
1582 end
1583
1584 AbstractValue.render(self, ...)
1585 end
1586
1587 function MultiValue.value(self, key, val)
1588 if luci.util.contains(self.keylist, key) then
1589 return
1590 end
1591
1592 val = val or key
1593 table.insert(self.keylist, tostring(key))
1594 table.insert(self.vallist, tostring(val))
1595 end
1596
1597 function MultiValue.valuelist(self, section)
1598 local val = self:cfgvalue(section)
1599
1600 if not(type(val) == "string") then
1601 return {}
1602 end
1603
1604 return luci.util.split(val, self.delimiter)
1605 end
1606
1607 function MultiValue.validate(self, val)
1608 val = (type(val) == "table") and val or {val}
1609
1610 local result
1611
1612 for i, value in ipairs(val) do
1613 if luci.util.contains(self.keylist, value) then
1614 result = result and (result .. self.delimiter .. value) or value
1615 end
1616 end
1617
1618 return result
1619 end
1620
1621
1622 StaticList = class(MultiValue)
1623
1624 function StaticList.__init__(self, ...)
1625 MultiValue.__init__(self, ...)
1626 self.cast = "table"
1627 self.valuelist = self.cfgvalue
1628
1629 if not self.override_scheme
1630 and self.map:get_scheme(self.section.sectiontype, self.option) then
1631 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1632 if self.value and vs.values and not self.override_values then
1633 for k, v in pairs(vs.values) do
1634 self:value(k, v)
1635 end
1636 end
1637 end
1638 end
1639
1640 function StaticList.validate(self, value)
1641 value = (type(value) == "table") and value or {value}
1642
1643 local valid = {}
1644 for i, v in ipairs(value) do
1645 if luci.util.contains(self.keylist, v) then
1646 table.insert(valid, v)
1647 end
1648 end
1649 return valid
1650 end
1651
1652
1653 DynamicList = class(AbstractValue)
1654
1655 function DynamicList.__init__(self, ...)
1656 AbstractValue.__init__(self, ...)
1657 self.template = "cbi/dynlist"
1658 self.cast = "table"
1659 self.keylist = {}
1660 self.vallist = {}
1661 end
1662
1663 function DynamicList.value(self, key, val)
1664 val = val or key
1665 table.insert(self.keylist, tostring(key))
1666 table.insert(self.vallist, tostring(val))
1667 end
1668
1669 function DynamicList.write(self, ...)
1670 self.map.proceed = true
1671 return AbstractValue.write(self, ...)
1672 end
1673
1674 function DynamicList.formvalue(self, section)
1675 local value = AbstractValue.formvalue(self, section)
1676 value = (type(value) == "table") and value or {value}
1677
1678 local valid = {}
1679 for i, v in ipairs(value) do
1680 if v and #v > 0
1681 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1682 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1683 table.insert(valid, v)
1684 end
1685 end
1686
1687 return valid
1688 end
1689
1690
1691 --[[
1692 TextValue - A multi-line value
1693 rows: Rows
1694 ]]--
1695 TextValue = class(AbstractValue)
1696
1697 function TextValue.__init__(self, ...)
1698 AbstractValue.__init__(self, ...)
1699 self.template = "cbi/tvalue"
1700 end
1701
1702 --[[
1703 Button
1704 ]]--
1705 Button = class(AbstractValue)
1706
1707 function Button.__init__(self, ...)
1708 AbstractValue.__init__(self, ...)
1709 self.template = "cbi/button"
1710 self.inputstyle = nil
1711 self.rmempty = true
1712 end
1713
1714
1715 FileUpload = class(AbstractValue)
1716
1717 function FileUpload.__init__(self, ...)
1718 AbstractValue.__init__(self, ...)
1719 self.template = "cbi/upload"
1720 if not self.map.upload_fields then
1721 self.map.upload_fields = { self }
1722 else
1723 self.map.upload_fields[#self.map.upload_fields+1] = self
1724 end
1725 end
1726
1727 function FileUpload.formcreated(self, section)
1728 return AbstractValue.formcreated(self, section) or
1729 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1730 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1731 end
1732
1733 function FileUpload.cfgvalue(self, section)
1734 local val = AbstractValue.cfgvalue(self, section)
1735 if val and fs.access(val) then
1736 return val
1737 end
1738 return nil
1739 end
1740
1741 function FileUpload.formvalue(self, section)
1742 local val = AbstractValue.formvalue(self, section)
1743 if val then
1744 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1745 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1746 then
1747 return val
1748 end
1749 fs.unlink(val)
1750 self.value = nil
1751 end
1752 return nil
1753 end
1754
1755 function FileUpload.remove(self, section)
1756 local val = AbstractValue.formvalue(self, section)
1757 if val and fs.access(val) then fs.unlink(val) end
1758 return AbstractValue.remove(self, section)
1759 end
1760
1761
1762 FileBrowser = class(AbstractValue)
1763
1764 function FileBrowser.__init__(self, ...)
1765 AbstractValue.__init__(self, ...)
1766 self.template = "cbi/browser"
1767 end