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