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