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